xorg-xserver

简介

        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()等许多显示相关元素集中初始化。

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值