Xrandr 与Xserver 的交互
在X Windows system 中,Xrandr 做 X client ,Xorg 作为server端。
1. RandR Externsion 的注册
对于Xorg 1.6.0,如果在xorg.conf文件中,没有显式的disable RandR。Xorg 会将Xrandr extension 加载。
InitExtensions
RRExtensionInit
AddExtension (RANDR_NAME, RRNumberEvents, RRNumberErrors,
ProcRRDispatch, SProcRRDispatch,
NULL, StandardMinorOpcode);
X11/extensions/randr.h:40 :define RANDR_NAME "RANDR"
此函数的作用是:为全局变量extensions 增加一个新的成员,
i = NumExtensions;
NumExtensions++;
extensions[i] = ext;
ext->index = i;
ext->base = i + EXTENSION_BASE(128);
strcpy(ext->name, name); 其中的name 即为RANDR.
ProcVector[i + EXTENSION_BASE] = MainProc即 ProcRRDispatch;
SwappedProcVector[i + EXTENSION_BASE] = SwappedMainProc 即 SProcRRDispatch;
其中ProcVector是函数指针数组。
1.1 ProcRRDispatch
static int
ProcRRDispatch (ClientPtr client)
{
REQUEST(xReq);
if (stuff->data >= RRNumberRequests || !ProcRandrVector[stuff->data])
return BadRequest;
return (*ProcRandrVector[stuff->data]) (client);
}
其中ProcRandrVector也是函数指针数组。
int (*ProcRandrVector[RRNumberRequests])(ClientPtr) = {
ProcRRQueryVersion, /* 0 */
/* we skip 1 to make old clients fail pretty immediately */
NULL, /* 1 ProcRandrOldGetScreenInfo */
/* V1.0 apps share the same set screen config request id */
ProcRRSetScreenConfig, /* 2 */
NULL, /* 3 ProcRandrOldScreenChangeSelectInput */
/* 3 used to be ScreenChangeSelectInput; deprecated */
……
}
可以看出该函数的作用是利用stuff->data 作为index 做为index 来选择不同的函数。
SProcRRDispatch
SProcRRDispatch 与 ProcRRDispatch的作用完全相同,在这里就不赘述了。但是不太明白两者的区别。
2. RandR 向X server 发出请求。
以 RandR函数 XRRGetScreenSizeRange 为例。
RandR填充数据结构
typedef struct {
CARD8 reqType;
CARD8 randrReqType;
CARD16 length B16;
Window window B32;
} xRRGetScreenSizeRangeReq;
req->reqType = info->codes->major_opcode;
req->randrReqType = X_RRGetScreenSizeRange(值是6);
req->window = window;
然后利用if (!_XReply (dpy, (xReply *) &rep, 0, xFalse))将请求发出。
Xserver 利用函数ReadRequestFromClient将client送来的request解析出来,并赋值给数据结构:
typedef struct _xReq {
CARD8 reqType;
CARD8 data; /* meaning depends on request type */
CARD16 length B16; /* length in 4 bytes quantities of whole request, including this header */
} xReq;
xReq *req = client->requestBuffer; 因此:
xReq-> reqType = req->reqType;
xReq-> data = req->randrReqType (6);
2.1 info->codes->major_opcode
info->codes->major_opcode值 其实为第一部分中,RandR Extension 的ext->base = i + EXTENSION_BASE(128)。 但是如何得到ext->base,确实费了一番功夫。
RandR 在赋值 req->reqType = info->codes->major_opcode前,调用函数XRRFindDisplay-> XextAddDisplay,在XextAddDisplay中,会向Xserver也发送一个请求,从而得到ext->base.(具体过程后面介绍)
3. Xserver 如何处理Xrandr的请求
这段代码没有看懂,以下是debug情况下的总结,我在终端是终端上输入命令xrandr –q。
WaitforSometing一直在循环等待,当有请求时。
首先调用EstablishNewConnections -àNextAvailableClient-à InitClient,在InitClient中将client->requestVector = InitialVector。
InitialVector的定义如下:
int (* InitialVector[3]) (
ClientPtr /* client */
) =
{
0,
ProcInitialConnection,
ProcEstablishConnection
};
Dispatch继续向下调用,455行,result = (* client->requestVector[MAJOROP])(client),即调用ProcEstablishConnection。(不明白MAJOROP为什么此时是2.)
ProcEstablishConnection调用函数SendConnSetup,SendConnSetup中
client->requestVector = client->swapped ? SwappedProcVector : ProcVector;。进行Vector的选择,本例中选择的是ProcVector。
在下一次Dispatch的循环中,455 行调用。
result = (* client->requestVector[MAJOROP])(client); ,其中#define MAJOROP ((xReq *)client->requestBuffer)->reqType ,从2.1可知,reqType为ext->base. 即刚才的语句调用了函数ProcRRDispatch。而在函数ProcRRDispatch又根据xReq-> data最终调用到函数ProcRRGetScreenSizeRange。
4. 如何得到 RandR Extension 的ext->base = i + EXTENSION_BASE(128)
以Randr中的函数XRRGetScreenInfo为例。
XRRGetScreenInfo-> XRRFindDisplay->XextAddDisplay (&XRRExtensionInfo, dpy,
XRRExtensionName, &rr_extension_hooks, RRNumberEvents, 0);
è XInitExtension -> XQueryExtension
XQueryExtension 首先利用GetReq(QueryExtension, req),将
req->reqType = X_##name;\,即req->reqType =X_QueryExternsion,(98)然后调用_XReply (dpy, (xReply *)&rep, 0, xTrue),向Xserver发出请求。
Xserver收到请求后,从ProcVector中找到index 为98的函数ProcQueryExtension,在这个函数中,会根据req中Externsion的名字(RandR),遍历全局变量extensions,找到RandR extension,并返回ext->base。