简介
xserver主要提供显示接口给xclient使用,并将用户的操作反应给xclient。是xclient与硬件之间的中间层。
xserver主要管理鼠标、键盘、显卡、显示器。
xserver主要有两部分构成:
xorg.conf: xorg.conf是X Server的主要配置文件,它包含一个当前系统的硬件资源列表。X Server就是根据这些硬件资源“组织”出基本的图形能力。
xsession: xsession是指xserver启动后直到xserver关闭之间的这段时间。这期间一切跟X相关的动作都属于xsession的内容。
示意图
启动X
我们一般使用startx从控制台来启动X。startx本质是一个bash脚本,该脚本位于usr/bin目录下。用法如下所示:
startx [ [ client ] options ... ] [ -- [ server ] [ display ] options ... ]
常用的三种启动方式如下所示:
1. 指定client和server,例如: startx /usr/bin/xclock -- /usr/bin/Xorg :0。
2. 创建$HOME/.xserverrc启动server,$HOME/.xinitrc启动的client。
3. startx,不指定任何参数,启动DE(如GNOME)。
startx最重要的任务即找到xserver和xclient的配置档,将相关参数传递给xinit去启动X。xinit先启动xserver,然后再启动xclient。
xserver启动流程源码分析
(代码基于xorg-server-21.1.4)
xserver的入口函数为main(),位于dix/stubmain.c中,其主要执行内容为dix_main(),位于dix/main.c中。
在dix_main.c中,首先执行的是一系列初始化及check工作:
InitRegions();
CheckUserParameters(argc, argv, envp);
CheckUserAuthorization();
ProcessCommandLine(argc, argv);
随后进入了一个while(1)循环,每次循环包含如下三个阶段:
1. xserver初始化
2. xserver循环处理client信息
3. xserver退出
一般启动一个xserver只会执行一遍循环,用户在图形界面操作时处于2阶段。当有特殊情况发生,比如在运行X时切换显卡驱动,则会终止第一次循环而开启第二次循环。
下面介绍下xserver初始化阶段的部分源码:
if (serverGeneration == 1) {
CreateWellKnownSockets(); //初始化sockets,监听是否有clients申请连接
for (i = 1; i < LimitClients; i++)
clients[i] = NullClient;
serverClient = calloc(sizeof(ClientRec), 1); //clients数组中索引为0的项,拥有 root window的client
if (!serverClient)
FatalError("couldn't create server client");
InitClient(serverClient, 0, (void *) NULL);
}
else
ResetWellKnownSockets();
当第一次循环时,serverGeneration=1,执行第一个分支代码;之后serverGeneration=0,执行第二个分支的代码。
InitOutput()是初始化阶段重要的一个环节,该函数位于hw/xfree86/common/xf86Init.c中,处理过程可分为如下环节:
1. xf86HandleConfigFile(),用于加载配置文件和填充全局数据结构体。其中,xf86readConfigFile()用于解析配置文件。
2. xf86BusProbe(), 获得video的pci信息,例如framebuffer地址等。
3. xf86ModulelistFromConfig()(3D)、xf86DriverlistFromConfig()(display driver)、 xf86InputDriverlistFromConfig()(input driver)从配置文件中获取所有指定加载的模块列表,随后调用xf86LoadModules()进行模块加载。
4. 遍历注册的各个driver,调用driver的identify(),driverFunc()。其中driverFunc会设置某些标志位,决定是否打开控制台。
5. 执行xf86OpenConsole()打开控制台;执行xf86EnableIO()打开IO设备节点。
6. 在xf86BusConfig()中的xf86CallDriverProbe()会去调用对应driver的probe()。
7. 随后匹配screen,主要是根据xorg.conf中配置的screen,查询是否有与其匹配的device。
8. 遍历screen,调用其匹配驱动的PreInit(),这样就完成了显卡驱动的预初始化。
9. 遍历screen,调用AddScreen(),分配screenInfo.screen[]的一项,调用其匹配驱动的ScreenInit(),这样驱动的初始化基本完成。
InitInput()是初始化输入设备,例如键盘和鼠标等。如果xorg.conf中有Section InputDevice配置,会按照其配置扫描加载设备。
下面介绍下xserver循环处理client消息:
在初始化阶段结束后,xserver便进入了循环处理阶段即Dispatch(),其环节如下所示:
1. 进入循环,等待接收消息:
while (!dispatchException)
2. 接收用户输入并发送给client:
if (InputCheckPending()) {
ProcessInputEvents();
FlushIfCriticalOutputPending();
}
3. 等待clients发送消息过来:
if (!WaitForSomething(clients_are_ready()))
continue;
4. 遍历每个发送信息的client,做如下处理
1)接受用户输入并发送
if (*icheck[0] != *icheck[1])
ProcessInputEvents();
FlushIfCriticalOutputPending();
2)获得client的请求号
result = ReadRequestFromClient(client);
3) 根据请求号调用队列中相应的处理函数
if (result > (maxBigRequestSize << 2))
result = BadLength;
else {
result = XaceHookDispatch(client, MAJOROP);
if (result == Success)
result = (* client->requestVector[MAJOROP])(client);
XaceHookAuditEnd(client, result);
}
4) 若处理函数返回异常则做异常处理
if (result != Success)
{
if (client->noClientException != Success)
CloseDownClient(client);
else
SendErrorToClient(client, MAJOROP,
MinorOpcodeOfRequest(client),
client->errorValue, result);
break;
}
}
5) 提交处理结果
FlushAllOutput();
xserver退出包含了一系列释放内存,关闭clients等操作,这里就不多做解析。
xorg.conf配置详解
基本格式如下所示:
Section "section name"
option1 "option1 name"
option2 "option2 name"
...
EndSection
下面将说明xorg.conf文件中使用的Section类型及每个类型可用的选项名称和选项值:
1. ServerLayout
“ServerLayout” Section主要用于建立X Server启动时的外观,如果文件中包含多个ServerLayout Section,则默认会使用第一个ServerLayout Section的设置。
以下是此区块的系统默认值,以及可供使用的选项说明:
Section "ServerLayout"
Identifier "Default Layout"
Screen "Default Screen"
InputDevice "Generic Keyboard"
InputDevice "Configured Mouse"
InputDevice "stylus" "SendCoreEvents"
InputDevice "cursor" "SendCoreEvents"
InputDevice "eraser" "SendCoreEvents"
EndSection
1) Identifier:此ServerLayout Section的唯一名称。
2) Screen:“Screen”Section指定的名称
3) InputDevice:在X Server中的“InputDevice”Section名称。通常在此仅有两行设置,即Mouse0和Keyboard0,也就是系统中的第一个鼠标和键盘,而其他的设备大多可以忽略。
2. Files
“Files”Section用于设置X Server服务的路径,如字体和颜色。以下是此区块的系统默认值,以及可供使用的选项说明:
Section "Files"
FontPath "/usr/share/X11/fonts/misc"
FontPath "/usr/share/X11/fonts/cyrillic"
FontPath "/usr/share/X11/fonts/100dpi/:unscaled"
FontPath "/usr/share/X11/fonts/75dpi/:unscaled"
FontPath "/usr/share/X11/fonts/Type1"
FontPath "/usr/share/X11/fonts/100dpi"
FontPath "/usr/share/X11/fonts/75dpi"
FontPath "/usr/share/fonts/X11/misc"
# path to defoma fonts
FontPath "/var/lib/defoma/x-ttcidfont-conf.d/dirs/TrueType"
EndSection
3. Module
“Module”Section主要用来告诉X Server应加载哪些模块。这些模块可以提供额外的服务功能,一般并不需要更改此处的值。此处使用的惟一选项为“Load”,它可用来加载模块。以下是此区块的系统默认值:
Section "Module"
Load "i2c"
Load "bitmap"
Load "ddc"
Load "dri"
Load "extmod"
Load "freetype"
Load "glx"
Load "int10"
Load "type1"
Load "vbe"
EndSection
4. InputDevice
“InputDevice”Section用于设置鼠标或键盘等输入设备,以便通过X Server提供信息给Linux系统,多数系统至少都存在两个InputDevice Section(鼠标和键盘)。
以下是此区块的系统默认值,以及可供使用的选项说明:
Section "InputDevice"
Identifier "Generic Keyboard"
Driver "kbd"
Option "CoreKeyboard"
Option "XkbRules" "xorg"
Option "XkbModel" "pc105"
Option "XkbLayout" "us"
Option "XkbOptions" "lv3:ralt_switch"
EndSection
Section "InputDevice"
Identifier "Configured Mouse"
Driver "mouse"
Option "CorePointer"
Option "Device" "/dev/input/mice"
Option "Protocol" "ExplorerPS/2"
Option "ZAxisMapping" "4 5"
Option "Emulate3Buttons" "true"
EndSection
Section "InputDevice"
Driver "wacom"
Identifier "stylus"
Option "Device" "/dev/wacom" # Change to
# /dev/input/event
# for USB
Option "Type" "stylus"
Option "ForceDevice" "ISDV4" # Tablet PC ONLY
EndSection
Section "InputDevice"
Driver "wacom"
Identifier "eraser"
Option "Device" "/dev/wacom" # Change to
# /dev/input/event
# for USB
Option "Type" "eraser"
Option "ForceDevice" "ISDV4" # Tablet PC ONLY
EndSection
Section "InputDevice"
Driver "wacom"
Identifier "cursor"
Option "Device" "/dev/wacom" # Change to
# /dev/input/event
# for USB
Option "Type" "cursor"
Option "ForceDevice" "ISDV4" # Tablet PC ONLY
EndSection
1) Identifier:设置设备的名称。通常这些名称后面都会加上一个数字,第一个设备的数字为0。例如,第一个键盘的Identifier为Keyboard0。
2) Driver:告诉X Server应该从哪里加载驱动程序。
在大多数的InputDevice Section中,尚有为数不等以“Option”为首的选项,并且包含特定的选项值。如果要启用这些选项功能,只要将每行开头的注释符号“#”去除即可。
5. Monitor
“Monitor”Section用于设置系统使用的显示器类型,设置此处选项时应特别留意,因为不适当的设置可能会给显示器造成损害。
以下是此区块的系统默认值,以及可供使用的选项说明:
Section "Monitor"
Identifier "Generic Monitor"
Option "DPMS"
HorizSync 28-51
VertRefresh 43-60
EndSection
1) Identifier:显示器的惟一名称。在这些名称后面都会加上一个数字,而第一个显示器的代表数字为0(Monitor0)。
2) VendorName:显示器制造商名称。
3) ModelName:显示器类型名称。
4) HorizSync:与显示器兼容的水平刷新频率范围,其单位为kHz。这个设置值会同时指出是否在此显示器中使用特定的Modeline值。
5) VertRefresh:与显示器兼容的垂直刷新频率范围,其单位为kHz。这个设置值会同时指出是否在此显示器中使用特定的Modeline值。
6. Device
“Device”Section用于设置显示卡的信息内容,在此文件中至少需要包含一个以上的Device Section。如果系统中包含多张显示卡,或一张显示卡上有多种设置值,则可以使用多个Device Section设置。
以下是此区块的系统默认值,以及可供使用的选项说明:
Section "Device"
Identifier "VMWare Inc [VMware SVGA II] PCI Display Adapter"
Driver "vmware"
BusID "PCI:0:15:0"
EndSection
1) Identifier:显示卡的惟一名称。
2) Driver:用来告诉X Server应从何处加载显示卡的驱动程序。
3) VendorName:显示卡制造商名称。
4) BoardName:显示卡类型名称
5) BusID:显示卡的总线位置,这个选项适用于多显示卡环境。
7. Screen
“Screen”Section合并了Device和Monitor的部分,以便能够形成成对的设置内容。在此文件中至少需要包含一个以上的Screen Section。
以下是此区块的系统默认值,以及可供使用的选项说明:
Section "Screen"
Identifier "Default Screen"
Device "VMWare Inc [VMware SVGA II] PCI Display Adapter"
Monitor "Generic Monitor"
DefaultDepth 24
SubSection "Display"
Depth 1
Modes "1024×768" "800×600" "640×480"
EndSubSection
SubSection "Display"
Depth 4
Modes "1024×768" "800×600" "640×480"
EndSubSection
SubSection "Display"
Depth 8
Modes "1024×768" "800×600" "640×480"
EndSubSection
SubSection "Display"
Depth 15
Modes "1024×768" "800×600" "640×480"
EndSubSection
SubSection "Display"
Depth 16
Modes "1024×768" "800×600" "640×480"
EndSubSection
SubSection "Display"
Depth 24
Modes "1024×768" "800×600" "640×480"
EndSubSection
EndSection
1) Identifier:定义一个“Screen”名称,以便在“ServerLayout”Section中进行参照。
2) Device:指定“Device”Section中的名称。
3) Monitor:指定“Monitor”Section中的名称。
4) DefaultDepth:默认的色深(Color Depth)位数。
8. DRI
Direct Rendering Infrastructure(DRI)是一种接口,它让3D软件可以使用新型显示设备的3D硬件加速功能。除此之外,DRI也能改善2D硬件加速的性能。但通常并不使用这个选项功能,除非在“Module”Section中打开DRI设置。以下是此区块的系统默认值:
Section "DRI"
Mode 0666
EndSection
xlib介绍及简单示例程序
xlib是对X协议的一个简单的封装,可以让程序员不用了解细节而编写图形相关程序。实际上程序员直接调用xlib编写xclient的程序很少,更多使用的是GTK+ ,QT等图形库。这些又是基于Xlib的图形库。
一个简单的xlib例子如下:
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
Display *d;
Window w;
XEvent e;
char *msg = "Hello, World!";
int s;
d = XOpenDisplay(NULL);
if (d == NULL) {
fprintf(stderr, "Cannot open display\n");
exit(1);
}
s = DefaultScreen(d);
w = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 100, 100, 1,
BlackPixel(d, s), WhitePixel(d, s));
XSelectInput(d, w, ExposureMask | KeyPressMask);
XMapWindow(d, w);
while (1) {
XNextEvent(d, &e);
if (e.type == Expose) {
XFillRectangle(d, w, DefaultGC(d, s), 20, 20, 10, 10);
XDrawString(d, w, DefaultGC(d, s), 50, 50, msg, strlen(msg));
}
if (e.type == KeyPress)
break;
}
XCloseDisplay(d);
return 0;
}
显卡驱动初始化代码解析
1. Probe()
static Bool
Probe(DriverPtr drv, int flags)
{
int i, numDevSections;
GDevPtr *devSections;
Bool foundScreen = FALSE;
const char *dev;
ScrnInfoPtr scrn = NULL;
/* For now, just bail out for PROBE_DETECT. */
if (flags & PROBE_DETECT)
return FALSE;
/*
* Find the config file Device sections that match this
* driver, and return if there are none.
*/
if ((numDevSections = xf86MatchDevice("modesetting", &devSections)) <= 0) {
return FALSE;
}
for (i = 0; i < numDevSections; i++) {
int entity_num;
dev = xf86FindOptionValue(devSections[i]->options, "kmsdev");
if (probe_hw(dev, NULL)) {
entity_num = xf86ClaimFbSlot(drv, 0, devSections[i], TRUE);
scrn = xf86ConfigFbEntity(scrn, 0, entity_num, NULL, NULL, NULL, NULL);
}
if (scrn) {
foundScreen = TRUE;
ms_setup_scrn_hooks(scrn);
scrn->Probe = Probe;
xf86DrvMsg(scrn->scrnIndex, X_INFO,
"using %s\n", dev ? dev : "default device");
ms_setup_entity(scrn, entity_num);
}
}
free(devSections);
return foundScreen;
}
1) xf86MatchDevice(), 与config file中deivce节点下driver名字做匹配。
2) probe_hw(),打开内核drm设备。
3) ms_setup_scrn_hooks(),关联ops function,如下所示:
static void
ms_setup_scrn_hooks(ScrnInfoPtr scrn)
{
scrn->driverVersion = 1;
scrn->driverName = "modesetting";
scrn->name = "modeset";
scrn->Probe = NULL;
scrn->PreInit = PreInit;
scrn->ScreenInit = ScreenInit;
scrn->SwitchMode = SwitchMode;
scrn->AdjustFrame = AdjustFrame;
scrn->EnterVT = EnterVT;
scrn->LeaveVT = LeaveVT;
scrn->FreeScreen = FreeScreen;
scrn->ValidMode = ValidMode;
}
2. Preinit()
static Bool
PreInit(ScrnInfoPtr pScrn, int flags)
{
modesettingPtr ms;
rgb defaultWeight = { 0, 0, 0 };
EntityInfoPtr pEnt;
uint64_t value = 0;
int ret;
int bppflags, connector_count;
int defaultdepth, defaultbpp;
if (pScrn->numEntities != 1)
return FALSE;
if (flags & PROBE_DETECT) {
return FALSE;
}
/* Allocate driverPrivate */
if (!GetRec(pScrn))
return FALSE;
pEnt = xf86GetEntityInfo(pScrn->entityList[0]);
ms = modesettingPTR(pScrn);
ms->SaveGeneration = -1;
ms->pEnt = pEnt;
ms->drmmode.is_secondary = FALSE;
pScrn->displayWidth = 640; /* default it */
if (xf86IsEntityShared(pScrn->entityList[0])) {
if (xf86IsPrimInitDone(pScrn->entityList[0]))
ms->drmmode.is_secondary = TRUE;
else
xf86SetPrimInitDone(pScrn->entityList[0]);
}
pScrn->monitor = pScrn->confScreen->monitor;
pScrn->progClock = TRUE;
pScrn->rgbBits = 8;
if (!ms_get_drm_master_fd(pScrn))
return FALSE;
ms->drmmode.fd = ms->fd;
if (!check_outputs(ms->fd, &connector_count))
return FALSE;
drmmode_get_default_bpp(pScrn, &ms->drmmode, &defaultdepth, &defaultbpp);
if (defaultdepth == 24 && defaultbpp == 24) {
ms->drmmode.force_24_32 = TRUE;
ms->drmmode.kbpp = 24;
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"Using 24bpp hw front buffer with 32bpp shadow\n");
defaultbpp = 32;
} else {
ms->drmmode.kbpp = 0;
}
bppflags = PreferConvert24to32 | SupportConvert24to32 | Support32bppFb;
if (!xf86SetDepthBpp
(pScrn, defaultdepth, defaultdepth, defaultbpp, bppflags))
return FALSE;
switch (pScrn->depth) {
case 15:
case 16:
case 24:
case 30:
break;
default:
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"Given depth (%d) is not supported by the driver\n",
pScrn->depth);
return FALSE;
}
xf86PrintDepthBpp(pScrn);
if (!ms->drmmode.kbpp)
ms->drmmode.kbpp = pScrn->bitsPerPixel;
/* Process the options */
xf86CollectOptions(pScrn, NULL);
if (!(ms->drmmode.Options = malloc(sizeof(Options))))
return FALSE;
memcpy(ms->drmmode.Options, Options, sizeof(Options));
xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, ms->drmmode.Options);
if (!xf86SetWeight(pScrn, defaultWeight, defaultWeight))
return FALSE;
if (!xf86SetDefaultVisual(pScrn, -1))
return FALSE;
if (xf86ReturnOptValBool(ms->drmmode.Options, OPTION_SW_CURSOR, FALSE)) {
ms->drmmode.sw_cursor = TRUE;
}
ms->cursor_width = 64;
ms->cursor_height = 64;
ret = drmGetCap(ms->fd, DRM_CAP_CURSOR_WIDTH, &value);
if (!ret) {
ms->cursor_width = value;
}
ret = drmGetCap(ms->fd, DRM_CAP_CURSOR_HEIGHT, &value);
if (!ret) {
ms->cursor_height = value;
}
try_enable_glamor(pScrn);
if (!ms->drmmode.glamor) {
Bool prefer_shadow = TRUE;
if (ms->drmmode.force_24_32) {
prefer_shadow = TRUE;
ms->drmmode.shadow_enable = TRUE;
} else {
ret = drmGetCap(ms->fd, DRM_CAP_DUMB_PREFER_SHADOW, &value);
if (!ret) {
prefer_shadow = !!value;
}
ms->drmmode.shadow_enable =
xf86ReturnOptValBool(ms->drmmode.Options, OPTION_SHADOW_FB,
prefer_shadow);
}
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"ShadowFB: preferred %s, enabled %s\n",
prefer_shadow ? "YES" : "NO",
ms->drmmode.force_24_32 ? "FORCE" :
ms->drmmode.shadow_enable ? "YES" : "NO");
ms->drmmode.shadow_enable2 = msShouldDoubleShadow(pScrn, ms);
} else {
if (!pScrn->is_gpu) {
MessageType from = xf86GetOptValBool(ms->drmmode.Options, OPTION_VARIABLE_REFRESH,
&ms->vrr_support) ? X_CONFIG : X_DEFAULT;
xf86DrvMsg(pScrn->scrnIndex, from, "VariableRefresh: %sabled\n",
ms->vrr_support ? "en" : "dis");
ms->drmmode.async_flip_secondaries = FALSE;
from = xf86GetOptValBool(ms->drmmode.Options, OPTION_ASYNC_FLIP_SECONDARIES,
&ms->drmmode.async_flip_secondaries) ? X_CONFIG : X_DEFAULT;
xf86DrvMsg(pScrn->scrnIndex, from, "AsyncFlipSecondaries: %sabled\n",
ms->drmmode.async_flip_secondaries ? "en" : "dis");
}
}
ms->drmmode.pageflip =
xf86ReturnOptValBool(ms->drmmode.Options, OPTION_PAGEFLIP, TRUE);
pScrn->capabilities = 0;
ret = drmGetCap(ms->fd, DRM_CAP_PRIME, &value);
if (ret == 0) {
if (connector_count && (value & DRM_PRIME_CAP_IMPORT)) {
pScrn->capabilities |= RR_Capability_SinkOutput;
if (ms->drmmode.glamor)
pScrn->capabilities |= RR_Capability_SinkOffload;
}
#ifdef GLAMOR_HAS_GBM_LINEAR
if (value & DRM_PRIME_CAP_EXPORT && ms->drmmode.glamor)
pScrn->capabilities |= RR_Capability_SourceOutput | RR_Capability_SourceOffload;
#endif
}
/*
* Use "atomic modesetting disable" request to detect if the kms driver is
* atomic capable, regardless if we will actually use atomic modesetting.
* This is effectively a no-op, we only care about the return status code.
*/
ret = drmSetClientCap(ms->fd, DRM_CLIENT_CAP_ATOMIC, 0);
ms->atomic_modeset_capable = (ret == 0);
if (xf86ReturnOptValBool(ms->drmmode.Options, OPTION_ATOMIC, FALSE)) {
ret = drmSetClientCap(ms->fd, DRM_CLIENT_CAP_ATOMIC, 1);
ms->atomic_modeset = (ret == 0);
} else {
ms->atomic_modeset = FALSE;
}
ms->kms_has_modifiers = FALSE;
ret = drmGetCap(ms->fd, DRM_CAP_ADDFB2_MODIFIERS, &value);
if (ret == 0 && value != 0)
ms->kms_has_modifiers = TRUE;
if (drmmode_pre_init(pScrn, &ms->drmmode, pScrn->bitsPerPixel / 8) == FALSE) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "KMS setup failed\n");
goto fail;
}
/*
* If the driver can do gamma correction, it should call xf86SetGamma() here.
*/
{
Gamma zeros = { 0.0, 0.0, 0.0 };
if (!xf86SetGamma(pScrn, zeros)) {
return FALSE;
}
}
if (!(pScrn->is_gpu && connector_count == 0) && pScrn->modes == NULL) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No modes.\n");
return FALSE;
}
pScrn->currentMode = pScrn->modes;
/* Set display resolution */
xf86SetDpi(pScrn, 0, 0);
/* Load the required sub modules */
if (!xf86LoadSubModule(pScrn, "fb")) {
return FALSE;
}
if (ms->drmmode.shadow_enable) {
void *mod = xf86LoadSubModule(pScrn, "shadow");
if (!mod)
return FALSE;
ms->shadow.Setup = LoaderSymbolFromModule(mod, "shadowSetup");
ms->shadow.Add = LoaderSymbolFromModule(mod, "shadowAdd");
ms->shadow.Remove = LoaderSymbolFromModule(mod, "shadowRemove");
ms->shadow.Update32to24 = LoaderSymbolFromModule(mod, "shadowUpdate32to24");
ms->shadow.UpdatePacked = LoaderSymbolFromModule(mod, "shadowUpdatePacked");
}
return TRUE;
fail:
return FALSE;
}
在Preinit()阶段,主要完成一些对显示参数的初始化及设置给内核, 如rgb的权重、位深、分辨率、gamma校准、光标大小等;同时还需要通过check outputs检查下connector情况;最后需要加载一些相关模块,比如fb、shadow等。
3. ScreenInit()
static Bool
ScreenInit(ScreenPtr pScreen, int argc, char **argv)
{
ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
modesettingPtr ms = modesettingPTR(pScrn);
VisualPtr visual;
pScrn->pScreen = pScreen;
if (!SetMaster(pScrn))
return FALSE;
#ifdef GLAMOR_HAS_GBM
if (ms->drmmode.glamor)
ms->drmmode.gbm = ms->glamor.egl_get_gbm_device(pScreen);
#endif
/* HW dependent - FIXME */
pScrn->displayWidth = pScrn->virtualX;
if (!drmmode_create_initial_bos(pScrn, &ms->drmmode))
return FALSE;
if (ms->drmmode.shadow_enable) {
ms->drmmode.shadow_fb =
calloc(1,
pScrn->displayWidth * pScrn->virtualY *
((pScrn->bitsPerPixel + 7) >> 3));
if (!ms->drmmode.shadow_fb)
ms->drmmode.shadow_enable = FALSE;
}
miClearVisualTypes();
if (!miSetVisualTypes(pScrn->depth,
miGetDefaultVisualMask(pScrn->depth),
pScrn->rgbBits, pScrn->defaultVisual))
return FALSE;
if (!miSetPixmapDepths())
return FALSE;
if (!dixRegisterScreenSpecificPrivateKey
(pScreen, &ms->drmmode.pixmapPrivateKeyRec, PRIVATE_PIXMAP,
sizeof(msPixmapPrivRec))) {
return FALSE;
}
pScrn->memPhysBase = 0;
pScrn->fbOffset = 0;
if (!fbScreenInit(pScreen, NULL,
pScrn->virtualX, pScrn->virtualY,
pScrn->xDpi, pScrn->yDpi,
pScrn->displayWidth, pScrn->bitsPerPixel))
return FALSE;
if (pScrn->bitsPerPixel > 8) {
/* Fixup RGB ordering */
visual = pScreen->visuals + pScreen->numVisuals;
while (--visual >= pScreen->visuals) {
if ((visual->class | DynamicClass) == DirectColor) {
visual->offsetRed = pScrn->offset.red;
visual->offsetGreen = pScrn->offset.green;
visual->offsetBlue = pScrn->offset.blue;
visual->redMask = pScrn->mask.red;
visual->greenMask = pScrn->mask.green;
visual->blueMask = pScrn->mask.blue;
}
}
}
fbPictureInit(pScreen, NULL, 0);
if (drmmode_init(pScrn, &ms->drmmode) == FALSE) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"Failed to initialize glamor at ScreenInit() time.\n");
return FALSE;
}
if (ms->drmmode.shadow_enable && !ms->shadow.Setup(pScreen)) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "shadow fb init failed\n");
return FALSE;
}
ms->createScreenResources = pScreen->CreateScreenResources;
pScreen->CreateScreenResources = CreateScreenResources;
xf86SetBlackWhitePixels(pScreen);
xf86SetBackingStore(pScreen);
xf86SetSilkenMouse(pScreen);
miDCInitialize(pScreen, xf86GetPointerScreenFuncs());
/* If pageflip is enabled hook the screen's cursor-sprite (swcursor) funcs.
* So that we can disable page-flipping on fallback to a swcursor. */
if (ms->drmmode.pageflip) {
miPointerScreenPtr PointPriv =
dixLookupPrivate(&pScreen->devPrivates, miPointerScreenKey);
if (!dixRegisterScreenPrivateKey(&ms->drmmode.spritePrivateKeyRec,
pScreen, PRIVATE_DEVICE,
sizeof(msSpritePrivRec)))
return FALSE;
ms->SpriteFuncs = PointPriv->spriteFuncs;
PointPriv->spriteFuncs = &drmmode_sprite_funcs;
}
/* Need to extend HWcursor support to handle mask interleave */
if (!ms->drmmode.sw_cursor)
xf86_cursors_init(pScreen, ms->cursor_width, ms->cursor_height,
HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_64 |
HARDWARE_CURSOR_UPDATE_UNHIDDEN |
HARDWARE_CURSOR_ARGB);
/* Must force it before EnterVT, so we are in control of VT and
* later memory should be bound when allocating, e.g rotate_mem */
pScrn->vtSema = TRUE;
if (serverGeneration == 1 && bgNoneRoot && ms->drmmode.glamor) {
ms->CreateWindow = pScreen->CreateWindow;
pScreen->CreateWindow = CreateWindow_oneshot;
}
pScreen->SaveScreen = xf86SaveScreen;
ms->CloseScreen = pScreen->CloseScreen;
pScreen->CloseScreen = CloseScreen;
ms->BlockHandler = pScreen->BlockHandler;
pScreen->BlockHandler = msBlockHandler_oneshot;
pScreen->SharePixmapBacking = msSharePixmapBacking;
pScreen->SetSharedPixmapBacking = msSetSharedPixmapBacking;
pScreen->StartPixmapTracking = PixmapStartDirtyTracking;
pScreen->StopPixmapTracking = PixmapStopDirtyTracking;
pScreen->SharedPixmapNotifyDamage = msSharedPixmapNotifyDamage;
pScreen->RequestSharedPixmapNotifyDamage =
msRequestSharedPixmapNotifyDamage;
pScreen->PresentSharedPixmap = msPresentSharedPixmap;
pScreen->StopFlippingPixmapTracking = msStopFlippingPixmapTracking;
if (!xf86CrtcScreenInit(pScreen))
return FALSE;
if (!drmmode_setup_colormap(pScreen, pScrn))
return FALSE;
if (ms->atomic_modeset)
xf86DPMSInit(pScreen, drmmode_set_dpms, 0);
else
xf86DPMSInit(pScreen, xf86DPMSSet, 0);
#ifdef GLAMOR_HAS_GBM
if (ms->drmmode.glamor) {
XF86VideoAdaptorPtr glamor_adaptor;
glamor_adaptor = ms->glamor.xv_init(pScreen, 16);
if (glamor_adaptor != NULL)
xf86XVScreenInit(pScreen, &glamor_adaptor, 1);
else
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"Failed to initialize XV support.\n");
}
#endif
if (serverGeneration == 1)
xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options);
if (!ms_vblank_screen_init(pScreen)) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"Failed to initialize vblank support.\n");
return FALSE;
}
#ifdef GLAMOR_HAS_GBM
if (ms->drmmode.glamor) {
if (!(ms->drmmode.dri2_enable = ms_dri2_screen_init(pScreen))) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"Failed to initialize the DRI2 extension.\n");
}
/* enable reverse prime if we are a GPU screen, and accelerated, and not
* i915, evdi or udl. i915 is happy scanning out from sysmem.
* evdi and udl are virtual drivers scanning out from sysmem
* backed dumb buffers.
*/
if (pScreen->isGPU) {
drmVersionPtr version;
/* enable if we are an accelerated GPU screen */
ms->drmmode.reverse_prime_offload_mode = TRUE;
if ((version = drmGetVersion(ms->drmmode.fd))) {
if (!strncmp("i915", version->name, version->name_len)) {
ms->drmmode.reverse_prime_offload_mode = FALSE;
}
if (!strncmp("evdi", version->name, version->name_len)) {
ms->drmmode.reverse_prime_offload_mode = FALSE;
}
if (!strncmp("udl", version->name, version->name_len)) {
ms->drmmode.reverse_prime_offload_mode = FALSE;
}
if (!ms->drmmode.reverse_prime_offload_mode) {
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"Disable reverse prime offload mode for %s.\n", version->name);
}
drmFreeVersion(version);
}
}
}
#endif
if (!(ms->drmmode.present_enable = ms_present_screen_init(pScreen))) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"Failed to initialize the Present extension.\n");
}
pScrn->vtSema = TRUE;
if (ms->vrr_support) {
if (!property_vectors_wrapped) {
saved_change_property = ProcVector[X_ChangeProperty];
ProcVector[X_ChangeProperty] = ms_change_property;
saved_delete_property = ProcVector[X_DeleteProperty];
ProcVector[X_DeleteProperty] = ms_delete_property;
property_vectors_wrapped = TRUE;
}
vrr_atom = MakeAtom("_VARIABLE_REFRESH",
strlen("_VARIABLE_REFRESH"), TRUE);
}
return TRUE;
}
这部分完成的初始化主要有:fbScreenInit()、显示图像像素相关的初始化、fbPictureInit()、drmmode_init()等许多显示相关元素集中初始化。