SDL2源代码分析1:初始化(SDL_Init())

SDL2 源码下载http://www.libsdl.org/download-2.0.php

=====================================================

SDL源代码分析系列文章列表:

SDL2源代码分析1:初始化(SDL_Init())

SDL2源代码分析2:窗口(SDL_Window)

SDL2源代码分析3:渲染器(SDL_Renderer)

SDL2源代码分析4:纹理(SDL_Texture)

SDL2源代码分析5:更新纹理(SDL_UpdateTexture())

SDL2源代码分析6:复制到渲染器(SDL_RenderCopy())

SDL2源代码分析7:显示(SDL_RenderPresent())

SDL2源代码分析8:视频显示总结

=====================================================


打算花一段时间研究一下SDL的内部代码。前面几篇文章《最简单的视音频播放示例1:总述》中记录了视频、音频播放的技术,文中提及了SDL实际上封装了Direct3D,DirectSound这类的底层API。但是SDL究竟是如何封装的呢?这次打算深入其源代码一探究竟,看看它是如何封装这些API的。

SDL2源代码分析1:初始化(SDL_Init())_第1张图片

SDL简介

有关SDL的简介在《最简单的视音频播放示例7:SDL2播放RGB/YUV》以及《最简单的视音频播放示例9:SDL2播放PCM》中已经叙述过了,不再重复。这两篇文章中也提到了一张SDL的原理图,如下所示:

SDL2源代码分析1:初始化(SDL_Init())_第2张图片

从这个图中可以看出,SDL根据系统的不同调用不同的API完成相应的功能。至于它是如何实现的,将会在后文中详细叙述。下面不再罗嗦,直接进入正题。
使用SDL播放一个视频代码流程大体如下
初始化: 
SDL_Init(): 初始化SDL。 
SDL_CreateWindow(): 创建窗口(Window)。 
SDL_CreateRenderer(): 基于窗口创建渲染器(Render)。 
SDL_CreateTexture(): 创建纹理(Texture)。 
循环渲染数据: 
SDL_UpdateTexture(): 设置纹理的数据。 
SDL_RenderCopy(): 纹理复制给渲染器。 
SDL_RenderPresent(): 显示。
本文分析这个流程中最基本的一个函数SDL_Init()。SDL_Init()是SDL运行的初始,通过分析该函数,可以了解到SDL内部的架构。


获取源代码

SDL的源代码获取十分简单。访问SDL的官方网站(http://www.libsdl.org/),单击左侧的“Download”进入下载页面,然后下载“SourceCode”栏目下的文件就可以了。

SDL2源代码分析1:初始化(SDL_Init())_第3张图片

下载下来的文件只有4MB左右大小,但是解压缩之后竟然有50MB左右大小,确实不可思议。

解压缩之后,源代码目录如下图所示。

SDL2源代码分析1:初始化(SDL_Init())_第4张图片

几个关键的文件夹如下所示:

1. include:存储SDL的头文件的文件夹。

2. src:存储SDL源代码文件的文件夹。SDL根据功能模块的不同,将源代码分成了很多的文件夹。下图中标出了存储SDL几个子系统的源代码的文件夹。

SDL2源代码分析1:初始化(SDL_Init())_第5张图片

3. VisualC:存储VC解决方案的文件夹。从下图中可以看出,包含了VS2008,VS2010,VS2012,VS2013等各个版本的VC的解决方案。

SDL2源代码分析1:初始化(SDL_Init())_第6张图片



实际上从文件名称我们可以看出,其它几个文件夹中,“Xcode,Xcode-iOS”包含了Xcode的项目文件,“test”包含了一些测试例子程序,“android-project”包含了Android下的项目文件。由于我们暂时不研究这些文件,就不详细分析了。

SDL_Init()


函数简介

下面这一部分进入正题,分析SDL的初始化函数SDL_Init()。该函数可以确定希望激活的子系统。SDL_Init()函数原型如下:

[cpp] view plain copy
print ?
  1. int SDLCALL SDL_Init(Uint32 flags)  
int SDLCALL SDL_Init(Uint32 flags)

其中,flags可以取下列值:
SDL_INIT_TIMER:定时器
SDL_INIT_AUDIO:音频
SDL_INIT_VIDEO:视频
SDL_INIT_JOYSTICK:摇杆
SDL_INIT_HAPTIC:触摸屏
SDL_INIT_GAMECONTROLLER:游戏控制器
SDL_INIT_EVENTS:事件
SDL_INIT_NOPARACHUTE:不捕获关键信号(这个不理解)
SDL_INIT_EVERYTHING:包含上述所有选项


函数调用关系图

SDL_Init()关键函数的调用关系可以用下图表示。

  SDL2源代码分析1:初始化(SDL_Init())_第7张图片

上面的函数调用关系图本来是一张高清大图,但是博客不支持这么大尺寸的图片。因此把图片缩小了,看上去比较模糊。相册里面上传了一份原始的大图片:

http://my.csdn.net/leixiaohua1020/album/detail/1792993

选择上述相册里面的图片,右键选择“另存为”即可保存原始大图片。


源代码分析

SDL_Init()的实现位于SDL.c中。定义如下。

[cpp] view plain copy
print ?
  1. int SDL_Init(Uint32 flags)  
  2. {  
  3.     return SDL_InitSubSystem(flags);  
  4. }  
int SDL_Init(Uint32 flags)
{
    return SDL_InitSubSystem(flags);
}

可以看出其代码只有一句,即调用了SDL_InitSubSystem(),下面我们看一下SDL_InitSubSystem()的定义。

[cpp] view plain copy
print ?
  1. int SDL_InitSubSystem(Uint32 flags)  
  2. {  
  3.     if (!SDL_MainIsReady) {  
  4.         SDL_SetError("Application didn't initialize properly, did you include SDL_main.h in the file containing your main() function?");  
  5.         return -1;  
  6.     }  
  7.   
  8.   
  9.     /* Clear the error message */  
  10.     SDL_ClearError();  
  11.   
  12.   
  13. #if SDL_VIDEO_DRIVER_WINDOWS  
  14.     if ((flags & (SDL_INIT_HAPTIC|SDL_INIT_JOYSTICK))) {  
  15.         if (SDL_HelperWindowCreate() < 0) {  
  16.             return -1;  
  17.         }  
  18.     }  
  19. #endif  
  20.   
  21.   
  22. #if !SDL_TIMERS_DISABLED  
  23.     SDL_TicksInit();  
  24. #endif  
  25.   
  26.   
  27.     if ((flags & SDL_INIT_GAMECONTROLLER)) {  
  28.         /* game controller implies joystick */  
  29.         flags |= SDL_INIT_JOYSTICK;  
  30.     }  
  31.   
  32.   
  33.     if ((flags & (SDL_INIT_VIDEO|SDL_INIT_JOYSTICK))) {  
  34.         /* video or joystick implies events */  
  35.         flags |= SDL_INIT_EVENTS;  
  36.     }  
  37.   
  38.   
  39.     /* Initialize the event subsystem */  
  40.     if ((flags & SDL_INIT_EVENTS)) {  
  41. #if !SDL_EVENTS_DISABLED  
  42.         if (SDL_PrivateShouldInitSubsystem(SDL_INIT_EVENTS)) {  
  43.             if (SDL_StartEventLoop() < 0) {  
  44.                 return (-1);  
  45.             }  
  46.             SDL_QuitInit();  
  47.         }  
  48.         SDL_PrivateSubsystemRefCountIncr(SDL_INIT_EVENTS);  
  49. #else  
  50.         return SDL_SetError("SDL not built with events support");  
  51. #endif  
  52.     }  
  53.   
  54.   
  55.     /* Initialize the timer subsystem */  
  56.     if ((flags & SDL_INIT_TIMER)){  
  57. #if !SDL_TIMERS_DISABLED  
  58.         if (SDL_PrivateShouldInitSubsystem(SDL_INIT_TIMER)) {  
  59.             if (SDL_TimerInit() < 0) {  
  60.                 return (-1);  
  61.             }  
  62.         }  
  63.         SDL_PrivateSubsystemRefCountIncr(SDL_INIT_TIMER);  
  64. #else  
  65.         return SDL_SetError("SDL not built with timer support");  
  66. #endif  
  67.     }  
  68.   
  69.   
  70.     /* Initialize the video subsystem */  
  71.     if ((flags & SDL_INIT_VIDEO)){  
  72. #if !SDL_VIDEO_DISABLED  
  73.         if (SDL_PrivateShouldInitSubsystem(SDL_INIT_VIDEO)) {  
  74.             if (SDL_VideoInit(NULL) < 0) {  
  75.                 return (-1);  
  76.             }  
  77.         }  
  78.         SDL_PrivateSubsystemRefCountIncr(SDL_INIT_VIDEO);  
  79. #else  
  80.         return SDL_SetError("SDL not built with video support");  
  81. #endif  
  82.     }  
  83.   
  84.   
  85.     /* Initialize the audio subsystem */  
  86.     if ((flags & SDL_INIT_AUDIO)){  
  87. #if !SDL_AUDIO_DISABLED  
  88.         if (SDL_PrivateShouldInitSubsystem(SDL_INIT_AUDIO)) {  
  89.             if (SDL_AudioInit(NULL) < 0) {  
  90.                 return (-1);  
  91.             }  
  92.         }  
  93.         SDL_PrivateSubsystemRefCountIncr(SDL_INIT_AUDIO);  
  94. #else  
  95.         return SDL_SetError("SDL not built with audio support");  
  96. #endif  
  97.     }  
  98.   
  99.   
  100.     /* Initialize the joystick subsystem */  
  101.     if ((flags & SDL_INIT_JOYSTICK)){  
  102. #if !SDL_JOYSTICK_DISABLED  
  103.         if (SDL_PrivateShouldInitSubsystem(SDL_INIT_JOYSTICK)) {  
  104.            if (SDL_JoystickInit() < 0) {  
  105.                return (-1);  
  106.            }  
  107.         }  
  108.         SDL_PrivateSubsystemRefCountIncr(SDL_INIT_JOYSTICK);  
  109. #else  
  110.         return SDL_SetError("SDL not built with joystick support");  
  111. #endif  
  112.     }  
  113.   
  114.   
  115.     if ((flags & SDL_INIT_GAMECONTROLLER)){  
  116. #if !SDL_JOYSTICK_DISABLED  
  117.         if (SDL_PrivateShouldInitSubsystem(SDL_INIT_GAMECONTROLLER)) {  
  118.             if (SDL_GameControllerInit() < 0) {  
  119.                 return (-1);  
  120.             }  
  121.         }  
  122.         SDL_PrivateSubsystemRefCountIncr(SDL_INIT_GAMECONTROLLER);  
  123. #else  
  124.         return SDL_SetError("SDL not built with joystick support");  
  125. #endif  
  126.     }  
  127.   
  128.   
  129.     /* Initialize the haptic subsystem */  
  130.     if ((flags & SDL_INIT_HAPTIC)){  
  131. #if !SDL_HAPTIC_DISABLED  
  132.         if (SDL_PrivateShouldInitSubsystem(SDL_INIT_HAPTIC)) {  
  133.             if (SDL_HapticInit() < 0) {  
  134.                 return (-1);  
  135.             }  
  136.         }  
  137.         SDL_PrivateSubsystemRefCountIncr(SDL_INIT_HAPTIC);  
  138. #else  
  139.         return SDL_SetError("SDL not built with haptic (force feedback) support");  
  140. #endif  
  141.     }  
  142.   
  143.   
  144.     return (0);  
  145. }  
int SDL_InitSubSystem(Uint32 flags)
{
    if (!SDL_MainIsReady) {
        SDL_SetError("Application didn't initialize properly, did you include SDL_main.h in the file containing your main() function?");
        return -1;
    }


    /* Clear the error message */
    SDL_ClearError();


#if SDL_VIDEO_DRIVER_WINDOWS
	if ((flags & (SDL_INIT_HAPTIC|SDL_INIT_JOYSTICK))) {
		if (SDL_HelperWindowCreate() < 0) {
			return -1;
		}
	}
#endif


#if !SDL_TIMERS_DISABLED
    SDL_TicksInit();
#endif


    if ((flags & SDL_INIT_GAMECONTROLLER)) {
        /* game controller implies joystick */
        flags |= SDL_INIT_JOYSTICK;
    }


    if ((flags & (SDL_INIT_VIDEO|SDL_INIT_JOYSTICK))) {
        /* video or joystick implies events */
        flags |= SDL_INIT_EVENTS;
    }


    /* Initialize the event subsystem */
    if ((flags & SDL_INIT_EVENTS)) {
#if !SDL_EVENTS_DISABLED
        if (SDL_PrivateShouldInitSubsystem(SDL_INIT_EVENTS)) {
            if (SDL_StartEventLoop() < 0) {
                return (-1);
            }
            SDL_QuitInit();
        }
        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_EVENTS);
#else
        return SDL_SetError("SDL not built with events support");
#endif
    }


    /* Initialize the timer subsystem */
    if ((flags & SDL_INIT_TIMER)){
#if !SDL_TIMERS_DISABLED
        if (SDL_PrivateShouldInitSubsystem(SDL_INIT_TIMER)) {
            if (SDL_TimerInit() < 0) {
                return (-1);
            }
        }
        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_TIMER);
#else
        return SDL_SetError("SDL not built with timer support");
#endif
    }


    /* Initialize the video subsystem */
    if ((flags & SDL_INIT_VIDEO)){
#if !SDL_VIDEO_DISABLED
        if (SDL_PrivateShouldInitSubsystem(SDL_INIT_VIDEO)) {
            if (SDL_VideoInit(NULL) < 0) {
                return (-1);
            }
        }
        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_VIDEO);
#else
        return SDL_SetError("SDL not built with video support");
#endif
    }


    /* Initialize the audio subsystem */
    if ((flags & SDL_INIT_AUDIO)){
#if !SDL_AUDIO_DISABLED
        if (SDL_PrivateShouldInitSubsystem(SDL_INIT_AUDIO)) {
            if (SDL_AudioInit(NULL) < 0) {
                return (-1);
            }
        }
        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_AUDIO);
#else
        return SDL_SetError("SDL not built with audio support");
#endif
    }


    /* Initialize the joystick subsystem */
    if ((flags & SDL_INIT_JOYSTICK)){
#if !SDL_JOYSTICK_DISABLED
        if (SDL_PrivateShouldInitSubsystem(SDL_INIT_JOYSTICK)) {
           if (SDL_JoystickInit() < 0) {
               return (-1);
           }
        }
        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_JOYSTICK);
#else
        return SDL_SetError("SDL not built with joystick support");
#endif
    }


    if ((flags & SDL_INIT_GAMECONTROLLER)){
#if !SDL_JOYSTICK_DISABLED
        if (SDL_PrivateShouldInitSubsystem(SDL_INIT_GAMECONTROLLER)) {
            if (SDL_GameControllerInit() < 0) {
                return (-1);
            }
        }
        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_GAMECONTROLLER);
#else
        return SDL_SetError("SDL not built with joystick support");
#endif
    }


    /* Initialize the haptic subsystem */
    if ((flags & SDL_INIT_HAPTIC)){
#if !SDL_HAPTIC_DISABLED
        if (SDL_PrivateShouldInitSubsystem(SDL_INIT_HAPTIC)) {
            if (SDL_HapticInit() < 0) {
                return (-1);
            }
        }
        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_HAPTIC);
#else
        return SDL_SetError("SDL not built with haptic (force feedback) support");
#endif
    }


    return (0);
}

SDL_InitSubSystem()函数的定义看上去很长,实际上却并不复杂。下面简单阐述一下它的一些关键点:

1. 通过将传入的flag与子系统的宏定义(例如SDL_INIT_VIDEO,SDL_INIT_AUDIO等)相与,判断是否需要初始化该子系统。

2. 有很多的预定义的宏,用于判断SDL是否支持这些子系统。例如SDL_EVENTS_DISABLED,SDL_TIMERS_DISABLED,SDL_VIDEO_DISABLED,SDL_AUDIO_DISABLED,SDL_JOYSTICK_DISABLED,SDL_HAPTIC_DISABLED等。这些宏的定义位于SDL_config_minimal.h文件中,如下所示。

[cpp] view plain copy
print ?
  1. /* Enable the dummy audio driver (src/audio/dummy/\*.c) */  
  2. #define SDL_AUDIO_DRIVER_DUMMY  1  
  3.   
  4.   
  5. /* Enable the stub joystick driver (src/joystick/dummy/\*.c) */  
  6. #define SDL_JOYSTICK_DISABLED   1  
  7.   
  8.   
  9. /* Enable the stub haptic driver (src/haptic/dummy/\*.c) */  
  10. #define SDL_HAPTIC_DISABLED 1  
  11.   
  12.   
  13. /* Enable the stub shared object loader (src/loadso/dummy/\*.c) */  
  14. #define SDL_LOADSO_DISABLED 1  
  15.   
  16.   
  17. /* Enable the stub thread support (src/thread/generic/\*.c) */  
  18. #define SDL_THREADS_DISABLED    1  
  19.   
  20.   
  21. /* Enable the stub timer support (src/timer/dummy/\*.c) */  
  22. #define SDL_TIMERS_DISABLED 1  
  23.   
  24.   
  25. /* Enable the dummy video driver (src/video/dummy/\*.c) */  
  26. #define SDL_VIDEO_DRIVER_DUMMY  1  
  27.   
  28.   
  29. /* Enable the dummy filesystem driver (src/filesystem/dummy/\*.c) */  
  30. #define SDL_FILESYSTEM_DUMMY  1  
/* Enable the dummy audio driver (src/audio/dummy/\*.c) */
#define SDL_AUDIO_DRIVER_DUMMY  1


/* Enable the stub joystick driver (src/joystick/dummy/\*.c) */
#define SDL_JOYSTICK_DISABLED   1


/* Enable the stub haptic driver (src/haptic/dummy/\*.c) */
#define SDL_HAPTIC_DISABLED 1


/* Enable the stub shared object loader (src/loadso/dummy/\*.c) */
#define SDL_LOADSO_DISABLED 1


/* Enable the stub thread support (src/thread/generic/\*.c) */
#define SDL_THREADS_DISABLED    1


/* Enable the stub timer support (src/timer/dummy/\*.c) */
#define SDL_TIMERS_DISABLED 1


/* Enable the dummy video driver (src/video/dummy/\*.c) */
#define SDL_VIDEO_DRIVER_DUMMY  1


/* Enable the dummy filesystem driver (src/filesystem/dummy/\*.c) */
#define SDL_FILESYSTEM_DUMMY  1

如果这些定义取值不为0,代表该子系统已经被disable了,就不编译指定子系统的源代码了。初始化的时候会调用SDL_SetError()函数输出错误信息。例如SDL_VIDEO_DISABLED如果设置为1的话,初始化视频子系统的时候会执行以下代码。
[cpp] view plain copy
print ?
  1. SDL_SetError("SDL not built with video support");  
SDL_SetError("SDL not built with video support");

3. 在每一个子系统真正初始化之前,都会调用一个函数SDL_PrivateShouldInitSubsystem()。该函数用于检查目标子系统是否需要初始化。

4. 在一个子系统初始化之后,都会调用一个函数SDL_PrivateSubsystemRefCountIncr()。该函数用于增加子系统的引用计数。

5. 下表列出各个子系统的初始化函数。

子系统名称

函数

AUDIO(音频)

SDL_AudioInit()

VIDEO(视频)

SDL_VideoInit()

TIMERS(定时器)

SDL_TicksInit(),SDL_TimerInit()

EVENTS(事件)

SDL_StartEventLoop()

JOYSTICK(摇杆)

SDL_GameControllerInit()

HAPTIC(触摸屏)

SDL_HapticInit()


我们先不看JOYSTICK(摇杆),HAPTIC(触摸屏)这些方面的代码,专门关注AUDIO(音频),VIDEO(视频)这两个方面的代码。

1.VIDEO(视频)

视频子系统的初始化函数是SDL_VideoInit()。它的源代码位于video\SDL_video.c文件中,如下所示。
[cpp] view plain copy
print ?
  1. /* 
  2.  * Initialize the video and event subsystems -- determine native pixel format 
  3.  */  
  4. int SDL_VideoInit(const char *driver_name)  
  5. {  
  6.     SDL_VideoDevice *video;  
  7.     const char *hint;  
  8.     int index;  
  9.     int i;  
  10.     SDL_bool allow_screensaver;  
  11.   
  12.   
  13.     /* Check to make sure we don't overwrite '_this' */  
  14.     if (_this != NULL) {  
  15.         SDL_VideoQuit();  
  16.     }  
  17.   
  18.   
  19. #if !SDL_TIMERS_DISABLED  
  20.     SDL_TicksInit();  
  21. #endif  
  22.   
  23.   
  24.     /* Start the event loop */  
  25.     if (SDL_InitSubSystem(SDL_INIT_EVENTS) < 0 ||  
  26.         SDL_KeyboardInit() < 0 ||  
  27.         SDL_MouseInit() < 0 ||  
  28.         SDL_TouchInit() < 0) {  
  29.         return -1;  
  30.     }  
  31.   
  32.   
  33.     /* Select the proper video driver */  
  34.     index = 0;  
  35.     video = NULL;  
  36.     if (driver_name == NULL) {  
  37.         driver_name = SDL_getenv("SDL_VIDEODRIVER");  
  38.     }  
  39.     if (driver_name != NULL) {  
  40.         for (i = 0; bootstrap[i]; ++i) {  
  41.             if (SDL_strncasecmp(bootstrap[i]->name, driver_name, SDL_strlen(driver_name)) == 0) {  
  42.                 if (bootstrap[i]->available()) {  
  43.                     video = bootstrap[i]->create(index);  
  44.                     break;  
  45.                 }  
  46.             }  
  47.         }  
  48.     } else {  
  49.         for (i = 0; bootstrap[i]; ++i) {  
  50.             if (bootstrap[i]->available()) {  
  51.                 video = bootstrap[i]->create(index);  
  52.                 if (video != NULL) {  
  53.                     break;  
  54.                 }  
  55.             }  
  56.         }  
  57.     }  
  58.     if (video == NULL) {  
  59.         if (driver_name) {  
  60.             return SDL_SetError("%s not available", driver_name);  
  61.         }  
  62.         return SDL_SetError("No available video device");  
  63.     }  
  64.     _this = video;  
  65.     _this->name = bootstrap[i]->name;  
  66.     _this->next_object_id = 1;  
  67.   
  68.   
  69.   
  70.   
  71.     /* Set some very sane GL defaults */  
  72.     _this->gl_config.driver_loaded = 0;  
  73.     _this->gl_config.dll_handle = NULL;  
  74.     SDL_GL_ResetAttributes();  
  75.   
  76.   
  77.     _this->current_glwin_tls = SDL_TLSCreate();  
  78.     _this->current_glctx_tls = SDL_TLSCreate();  
  79.   
  80.   
  81.     /* Initialize the video subsystem */  
  82.     if (_this->VideoInit(_this) < 0) {  
  83.         SDL_VideoQuit();  
  84.         return -1;  
  85.     }  
  86.   
  87.   
  88.     /* Make sure some displays were added */  
  89.     if (_this->num_displays == 0) {  
  90.         SDL_VideoQuit();  
  91.         return SDL_SetError("The video driver did not add any displays");  
  92.     }  
  93.   
  94.   
  95.     /* Add the renderer framebuffer emulation if desired */  
  96.     if (ShouldUseTextureFramebuffer()) {  
  97.         _this->CreateWindowFramebuffer = SDL_CreateWindowTexture;  
  98.         _this->UpdateWindowFramebuffer = SDL_UpdateWindowTexture;  
  99.         _this->DestroyWindowFramebuffer = SDL_DestroyWindowTexture;  
  100.     }  
  101.   
  102.   
  103.     /* Disable the screen saver by default. This is a change from <= 2.0.1, 
  104.        but most things using SDL are games or media players; you wouldn't 
  105.        want a screensaver to trigger if you're playing exclusively with a 
  106.        joystick, or passively watching a movie. Things that use SDL but 
  107.        function more like a normal desktop app should explicitly reenable the 
  108.        screensaver. */  
  109.     hint = SDL_GetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER);  
  110.     if (hint) {  
  111.         allow_screensaver = SDL_atoi(hint) ? SDL_TRUE : SDL_FALSE;  
  112.     } else {  
  113.         allow_screensaver = SDL_FALSE;  
  114.     }  
  115.     if (!allow_screensaver) {  
  116.         SDL_DisableScreenSaver();  
  117.     }  
  118.   
  119.   
  120.     /* If we don't use a screen keyboard, turn on text input by default, 
  121.        otherwise programs that expect to get text events without enabling 
  122.        UNICODE input won't get any events. 
  123.  
  124.  
  125.        Actually, come to think of it, you needed to call SDL_EnableUNICODE(1) 
  126.        in SDL 1.2 before you got text input events.  Hmm... 
  127.      */  
  128.     if (!SDL_HasScreenKeyboardSupport()) {  
  129.         SDL_StartTextInput();  
  130.     }  
  131.   
  132.   
  133.     /* We're ready to go! */  
  134.     return 0;  
  135. }  
/*
 * Initialize the video and event subsystems -- determine native pixel format
 */
int SDL_VideoInit(const char *driver_name)
{
    SDL_VideoDevice *video;
    const char *hint;
    int index;
    int i;
    SDL_bool allow_screensaver;


    /* Check to make sure we don't overwrite '_this' */
    if (_this != NULL) {
        SDL_VideoQuit();
    }


#if !SDL_TIMERS_DISABLED
    SDL_TicksInit();
#endif


    /* Start the event loop */
    if (SDL_InitSubSystem(SDL_INIT_EVENTS) < 0 ||
        SDL_KeyboardInit() < 0 ||
        SDL_MouseInit() < 0 ||
        SDL_TouchInit() < 0) {
        return -1;
    }


    /* Select the proper video driver */
    index = 0;
    video = NULL;
    if (driver_name == NULL) {
        driver_name = SDL_getenv("SDL_VIDEODRIVER");
    }
    if (driver_name != NULL) {
        for (i = 0; bootstrap[i]; ++i) {
            if (SDL_strncasecmp(bootstrap[i]->name, driver_name, SDL_strlen(driver_name)) == 0) {
                if (bootstrap[i]->available()) {
                    video = bootstrap[i]->create(index);
                    break;
                }
            }
        }
    } else {
        for (i = 0; bootstrap[i]; ++i) {
            if (bootstrap[i]->available()) {
                video = bootstrap[i]->create(index);
                if (video != NULL) {
                    break;
                }
            }
        }
    }
    if (video == NULL) {
        if (driver_name) {
            return SDL_SetError("%s not available", driver_name);
        }
        return SDL_SetError("No available video device");
    }
    _this = video;
    _this->name = bootstrap[i]->name;
    _this->next_object_id = 1;




    /* Set some very sane GL defaults */
    _this->gl_config.driver_loaded = 0;
    _this->gl_config.dll_handle = NULL;
    SDL_GL_ResetAttributes();


    _this->current_glwin_tls = SDL_TLSCreate();
    _this->current_glctx_tls = SDL_TLSCreate();


    /* Initialize the video subsystem */
    if (_this->VideoInit(_this) < 0) {
        SDL_VideoQuit();
        return -1;
    }


    /* Make sure some displays were added */
    if (_this->num_displays == 0) {
        SDL_VideoQuit();
        return SDL_SetError("The video driver did not add any displays");
    }


    /* Add the renderer framebuffer emulation if desired */
    if (ShouldUseTextureFramebuffer()) {
        _this->CreateWindowFramebuffer = SDL_CreateWindowTexture;
        _this->UpdateWindowFramebuffer = SDL_UpdateWindowTexture;
        _this->DestroyWindowFramebuffer = SDL_DestroyWindowTexture;
    }


    /* Disable the screen saver by default. This is a change from <= 2.0.1,
       but most things using SDL are games or media players; you wouldn't
       want a screensaver to trigger if you're playing exclusively with a
       joystick, or passively watching a movie. Things that use SDL but
       function more like a normal desktop app should explicitly reenable the
       screensaver. */
    hint = SDL_GetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER);
    if (hint) {
        allow_screensaver = SDL_atoi(hint) ? SDL_TRUE : SDL_FALSE;
    } else {
        allow_screensaver = SDL_FALSE;
    }
    if (!allow_screensaver) {
        SDL_DisableScreenSaver();
    }


    /* If we don't use a screen keyboard, turn on text input by default,
       otherwise programs that expect to get text events without enabling
       UNICODE input won't get any events.


       Actually, come to think of it, you needed to call SDL_EnableUNICODE(1)
       in SDL 1.2 before you got text input events.  Hmm...
     */
    if (!SDL_HasScreenKeyboardSupport()) {
        SDL_StartTextInput();
    }


    /* We're ready to go! */
    return 0;
}

下面简单阐述一下它的大致步骤:
1. 初始化一些子系统,比如EVENTS(事件)子系统。也就是说,就算在调用SDL_Init()的时候不指定初始化EVENTS子系统,在初始化VIDEO子系统的时候,同样也会初始化EVENTS子系统。
2. 选择一个合适的SDL_VideoDevice。
在这里,涉及到两个重要的结构体:SDL_VideoDevice以及VideoBootStrap。其中SDL_VideoDevice代表了一个视频驱动程序。VideoBootStrap从字面上理解是“视频驱动程序的引导程序”,即用于创建一个SDL_VideoDevice。因此,我们先来看看VideoBootStrap这个结构体。它的定义如下(位于video\SDL_sysvideo.h)。
[cpp] view plain copy
print ?
  1. typedef struct VideoBootStrap  
  2. {  
  3.     const char *name;  
  4.     const char *desc;  
  5.     int (*available) (void);  
  6.     SDL_VideoDevice *(*create) (int devindex);  
  7. } VideoBootStrap;  
typedef struct VideoBootStrap
{
    const char *name;
    const char *desc;
    int (*available) (void);
    SDL_VideoDevice *(*create) (int devindex);
} VideoBootStrap;

可以看出它的定义比较简单,每个字段的含义如下:
name:驱动名称
desc:描述
available():检查是否可用的一个函数指针
create():创建SDL_VideoDevice的函数指针


SDL中有一个VideoBootStrap类型的静态数组bootstrap。用于存储SDL支持的视频驱动程序。注意这是SDL“跨平台”特性中很重要的一部分。该静态数组定义如下(位于video\SDL_video.c)。
[cpp] view plain copy
print ?
  1. /* Available video drivers */  
  2. static VideoBootStrap *bootstrap[] = {  
  3. #if SDL_VIDEO_DRIVER_COCOA  
  4.     &COCOA_bootstrap,  
  5. #endif  
  6. #if SDL_VIDEO_DRIVER_X11  
  7.     &X11_bootstrap,  
  8. #endif  
  9. #if SDL_VIDEO_DRIVER_MIR  
  10.     &MIR_bootstrap,  
  11. #endif  
  12. #if SDL_VIDEO_DRIVER_DIRECTFB  
  13.     &DirectFB_bootstrap,  
  14. #endif  
  15. #if SDL_VIDEO_DRIVER_WINDOWS  
  16.     &WINDOWS_bootstrap,  
  17. #endif  
  18. #if SDL_VIDEO_DRIVER_WINRT  
  19.     &WINRT_bootstrap,  
  20. #endif  
  21. #if SDL_VIDEO_DRIVER_HAIKU  
  22.     &HAIKU_bootstrap,  
  23. #endif  
  24. #if SDL_VIDEO_DRIVER_PANDORA  
  25.     &PND_bootstrap,  
  26. #endif  
  27. #if SDL_VIDEO_DRIVER_UIKIT  
  28.     &UIKIT_bootstrap,  
  29. #endif  
  30. #if SDL_VIDEO_DRIVER_ANDROID  
  31.     &Android_bootstrap,  
  32. #endif  
  33. #if SDL_VIDEO_DRIVER_PSP  
  34.     &PSP_bootstrap,  
  35. #endif  
  36. #if SDL_VIDEO_DRIVER_RPI  
  37.     &RPI_bootstrap,  
  38. #endif   
  39. #if SDL_VIDEO_DRIVER_WAYLAND  
  40.     &Wayland_bootstrap,  
  41. #endif  
  42. #if SDL_VIDEO_DRIVER_DUMMY  
  43.     &DUMMY_bootstrap,  
  44. #endif  
  45.     NULL  
  46. };  
/* Available video drivers */
static VideoBootStrap *bootstrap[] = {
#if SDL_VIDEO_DRIVER_COCOA
    &COCOA_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_X11
    &X11_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_MIR
    &MIR_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_DIRECTFB
    &DirectFB_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_WINDOWS
    &WINDOWS_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_WINRT
    &WINRT_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_HAIKU
    &HAIKU_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_PANDORA
    &PND_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_UIKIT
    &UIKIT_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_ANDROID
    &Android_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_PSP
    &PSP_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_RPI
    &RPI_bootstrap,
#endif 
#if SDL_VIDEO_DRIVER_WAYLAND
    &Wayland_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_DUMMY
    &DUMMY_bootstrap,
#endif
    NULL
};


从代码中可以看出,SDL通过预编译宏取值是否非0来判断是否支持该视频驱动。我们可以看一下Windows的视频设备驱动的定义。该设备驱动同样是一个静态变量,名称为WINDOWS_bootstrap(位于video\windows\SDL_windowsvideo.c)。
[cpp] view plain copy
print ?
  1. VideoBootStrap WINDOWS_bootstrap = {  
  2.     "windows""SDL Windows video driver", WIN_Available, WIN_CreateDevice  
  3. };  
VideoBootStrap WINDOWS_bootstrap = {
    "windows", "SDL Windows video driver", WIN_Available, WIN_CreateDevice
};

可以看出该视频驱动名称为“windows”,描述为“SDL Windows video driver”,检查是否可用的函数为“WIN_Available()”,创建SDL_VideoDevice的函数为“WIN_CreateDevice()”。
同样, Android的视频设备驱动的名称为Android_bootstrap;PSP的视频设备驱动为PSP_bootstrap;X11的视频设备驱动为X11_bootstrap。不再一一例举。
下面看一下Windows视频驱动中那两个函数的定义。WIN_Available()定义如下。
[cpp] view plain copy
print ?
  1. static int WIN_Available(void)  
  2. {  
  3.     return (1);  
  4. }  
static int WIN_Available(void)
{
    return (1);
}

可见该函数没有做任何的处理。WIN_CreateDevice()定义如下。

[cpp] view plain copy
print ?
  1. static SDL_VideoDevice *  
  2. WIN_CreateDevice(int devindex)  
  3. {  
  4.     SDL_VideoDevice *device;  
  5.     SDL_VideoData *data;  
  6.   
  7.   
  8.     SDL_RegisterApp(NULL, 0, NULL);  
  9.   
  10.   
  11.     /* Initialize all variables that we clean on shutdown */  
  12.     device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));  
  13.     if (device) {  
  14.         data = (struct SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));  
  15.     } else {  
  16.         data = NULL;  
  17.     }  
  18.     if (!data) {  
  19.         SDL_free(device);  
  20.         SDL_OutOfMemory();  
  21.         return NULL;  
  22.     }  
  23.     device->driverdata = data;  
  24.   
  25.   
  26.     data->userDLL = SDL_LoadObject("USER32.DLL");  
  27.     if (data->userDLL) {  
  28.         data->CloseTouchInputHandle = (BOOL (WINAPI *)( HTOUCHINPUT )) SDL_LoadFunction(data->userDLL, "CloseTouchInputHandle");  
  29.         data->GetTouchInputInfo = (BOOL (WINAPI *)( HTOUCHINPUT, UINT, PTOUCHINPUT, int )) SDL_LoadFunction(data->userDLL, "GetTouchInputInfo");  
  30.         data->RegisterTouchWindow = (BOOL (WINAPI *)( HWNDULONG )) SDL_LoadFunction(data->userDLL, "RegisterTouchWindow");  
  31.     }  
  32.   
  33.   
  34.     /* Set the function pointers */  
  35.     device->VideoInit = WIN_VideoInit;  
  36.     device->VideoQuit = WIN_VideoQuit;  
  37.     device->GetDisplayBounds = WIN_GetDisplayBounds;  
  38.     device->GetDisplayModes = WIN_GetDisplayModes;  
  39.     device->SetDisplayMode = WIN_SetDisplayMode;  
  40.     device->PumpEvents = WIN_PumpEvents;  
  41.   
  42.   
  43. #undef CreateWindow  
  44.     device->CreateWindow = WIN_CreateWindow;  
  45.     device->CreateWindowFrom = WIN_CreateWindowFrom;  
  46.     device->SetWindowTitle = WIN_SetWindowTitle;  
  47.     device->SetWindowIcon = WIN_SetWindowIcon;  
  48.     device->SetWindowPosition = WIN_SetWindowPosition;  
  49.     device->SetWindowSize = WIN_SetWindowSize;  
  50.     device->ShowWindow = WIN_ShowWindow;  
  51.     device->HideWindow = WIN_HideWindow;  
  52.     device->RaiseWindow = WIN_RaiseWindow;  
  53.     device->MaximizeWindow = WIN_MaximizeWindow;  
  54.     device->MinimizeWindow = WIN_MinimizeWindow;  
  55.     device->RestoreWindow = WIN_RestoreWindow;  
  56.     device->SetWindowBordered = WIN_SetWindowBordered;  
  57.     device->SetWindowFullscreen = WIN_SetWindowFullscreen;  
  58.     device->SetWindowGammaRamp = WIN_SetWindowGammaRamp;  
  59.     device->GetWindowGammaRamp = WIN_GetWindowGammaRamp;  
  60.     device->SetWindowGrab = WIN_SetWindowGrab;  
  61.     device->DestroyWindow = WIN_DestroyWindow;  
  62.     device->GetWindowWMInfo = WIN_GetWindowWMInfo;  
  63.     device->CreateWindowFramebuffer = WIN_CreateWindowFramebuffer;  
  64.     device->UpdateWindowFramebuffer = WIN_UpdateWindowFramebuffer;  
  65.     device->DestroyWindowFramebuffer = WIN_DestroyWindowFramebuffer;  
  66.     device->OnWindowEnter = WIN_OnWindowEnter;  
  67.   
  68.   
  69.     device->shape_driver.CreateShaper = Win32_CreateShaper;  
  70.     device->shape_driver.SetWindowShape = Win32_SetWindowShape;  
  71.     device->shape_driver.ResizeWindowShape = Win32_ResizeWindowShape;  
  72.   
  73.   
  74. #if SDL_VIDEO_OPENGL_WGL  
  75.     device->GL_LoadLibrary = WIN_GL_LoadLibrary;  
  76.     device->GL_GetProcAddress = WIN_GL_GetProcAddress;  
  77.     device->GL_UnloadLibrary = WIN_GL_UnloadLibrary;  
  78.     device->GL_CreateContext = WIN_GL_CreateContext;  
  79.     device->GL_MakeCurrent = WIN_GL_MakeCurrent;  
  80.     device->GL_SetSwapInterval = WIN_GL_SetSwapInterval;  
  81.     device->GL_GetSwapInterval = WIN_GL_GetSwapInterval;  
  82.     device->GL_SwapWindow = WIN_GL_SwapWindow;  
  83.     device->GL_DeleteContext = WIN_GL_DeleteContext;  
  84. #endif  
  85.     device->StartTextInput = WIN_StartTextInput;  
  86.     device->StopTextInput = WIN_StopTextInput;  
  87.     device->SetTextInputRect = WIN_SetTextInputRect;  
  88.   
  89.   
  90.     device->SetClipboardText = WIN_SetClipboardText;  
  91.     device->GetClipboardText = WIN_GetClipboardText;  
  92.     device->HasClipboardText = WIN_HasClipboardText;  
  93.   
  94.   
  95.     device->free = WIN_DeleteDevice;  
  96.   
  97.   
  98.     return device;  
  99. }  
static SDL_VideoDevice *
WIN_CreateDevice(int devindex)
{
    SDL_VideoDevice *device;
    SDL_VideoData *data;


    SDL_RegisterApp(NULL, 0, NULL);


    /* Initialize all variables that we clean on shutdown */
    device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
    if (device) {
        data = (struct SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
    } else {
        data = NULL;
    }
    if (!data) {
        SDL_free(device);
        SDL_OutOfMemory();
        return NULL;
    }
    device->driverdata = data;


    data->userDLL = SDL_LoadObject("USER32.DLL");
    if (data->userDLL) {
        data->CloseTouchInputHandle = (BOOL (WINAPI *)( HTOUCHINPUT )) SDL_LoadFunction(data->userDLL, "CloseTouchInputHandle");
        data->GetTouchInputInfo = (BOOL (WINAPI *)( HTOUCHINPUT, UINT, PTOUCHINPUT, int )) SDL_LoadFunction(data->userDLL, "GetTouchInputInfo");
        data->RegisterTouchWindow = (BOOL (WINAPI *)( HWND, ULONG )) SDL_LoadFunction(data->userDLL, "RegisterTouchWindow");
    }


    /* Set the function pointers */
    device->VideoInit = WIN_VideoInit;
    device->VideoQuit = WIN_VideoQuit;
    device->GetDisplayBounds = WIN_GetDisplayBounds;
    device->GetDisplayModes = WIN_GetDisplayModes;
    device->SetDisplayMode = WIN_SetDisplayMode;
    device->PumpEvents = WIN_PumpEvents;


#undef CreateWindow
    device->CreateWindow = WIN_CreateWindow;
    device->CreateWindowFrom = WIN_CreateWindowFrom;
    device->SetWindowTitle = WIN_SetWindowTitle;
    device->SetWindowIcon = WIN_SetWindowIcon;
    device->SetWindowPosition = WIN_SetWindowPosition;
    device->SetWindowSize = WIN_SetWindowSize;
    device->ShowWindow = WIN_ShowWindow;
    device->HideWindow = WIN_HideWindow;
    device->RaiseWindow = WIN_RaiseWindow;
    device->MaximizeWindow = WIN_MaximizeWindow;
    device->MinimizeWindow = WIN_MinimizeWindow;
    device->RestoreWindow = WIN_RestoreWindow;
    device->SetWindowBordered = WIN_SetWindowBordered;
    device->SetWindowFullscreen = WIN_SetWindowFullscreen;
    device->SetWindowGammaRamp = WIN_SetWindowGammaRamp;
    device->GetWindowGammaRamp = WIN_GetWindowGammaRamp;
    device->SetWindowGrab = WIN_SetWindowGrab;
    device->DestroyWindow = WIN_DestroyWindow;
    device->GetWindowWMInfo = WIN_GetWindowWMInfo;
    device->CreateWindowFramebuffer = WIN_CreateWindowFramebuffer;
    device->UpdateWindowFramebuffer = WIN_UpdateWindowFramebuffer;
    device->DestroyWindowFramebuffer = WIN_DestroyWindowFramebuffer;
    device->OnWindowEnter = WIN_OnWindowEnter;


    device->shape_driver.CreateShaper = Win32_CreateShaper;
    device->shape_driver.SetWindowShape = Win32_SetWindowShape;
    device->shape_driver.ResizeWindowShape = Win32_ResizeWindowShape;


#if SDL_VIDEO_OPENGL_WGL
    device->GL_LoadLibrary = WIN_GL_LoadLibrary;
    device->GL_GetProcAddress = WIN_GL_GetProcAddress;
    device->GL_UnloadLibrary = WIN_GL_UnloadLibrary;
    device->GL_CreateContext = WIN_GL_CreateContext;
    device->GL_MakeCurrent = WIN_GL_MakeCurrent;
    device->GL_SetSwapInterval = WIN_GL_SetSwapInterval;
    device->GL_GetSwapInterval = WIN_GL_GetSwapInterval;
    device->GL_SwapWindow = WIN_GL_SwapWindow;
    device->GL_DeleteContext = WIN_GL_DeleteContext;
#endif
    device->StartTextInput = WIN_StartTextInput;
    device->StopTextInput = WIN_StopTextInput;
    device->SetTextInputRect = WIN_SetTextInputRect;


    device->SetClipboardText = WIN_SetClipboardText;
    device->GetClipboardText = WIN_GetClipboardText;
    device->HasClipboardText = WIN_HasClipboardText;


    device->free = WIN_DeleteDevice;


    return device;
}

该函数首先通过SDL_calloc()的方法为创建的SDL_VideoDevice分配了一块内存,接下来为创建的SDL_VideoDevice结构体中的函数指针赋了一大堆的值。这也是SDL“跨平台”特性的一个特点:通过调用SDL_VideoDevice中的接口函数,就可以调用不同平台的具体实现功能的函数。

PS:在这里补充一个SDL中内存分配函数的知识。在SDL中分配内存使用SDL_malloc(),SDL_calloc(),这些函数实际上就是malloc(),calloc()。它们的定义位于stdlib\SDL_malloc.c文件中。如下所示:

[cpp] view plain copy
print ?
  1. #define memset  SDL_memset  
  2. #define memcpy  SDL_memcpy  
  3. #define malloc  SDL_malloc  
  4. #define calloc  SDL_calloc  
  5. #define realloc SDL_realloc  
  6. #define free    SDL_free  
#define memset  SDL_memset
#define memcpy  SDL_memcpy
#define malloc  SDL_malloc
#define calloc  SDL_calloc
#define realloc SDL_realloc
#define free    SDL_free

下面来看一下SDL_VideoDevice这个结构体的定义(位于video\SDL_sysvideo.h)。
[cpp] view plain copy
print ?
  1. struct SDL_VideoDevice  
  2. {  
  3.     /* * * */  
  4.     /* The name of this video driver */  
  5.     const char *name;  
  6.   
  7.   
  8.     /* * * */  
  9.     /* Initialization/Query functions */  
  10.   
  11.   
  12.     /* 
  13.      * Initialize the native video subsystem, filling in the list of 
  14.      * displays for this driver, returning 0 or -1 if there's an error. 
  15.      */  
  16.     int (*VideoInit) (_THIS);  
  17.   
  18.   
  19.     /* 
  20.      * Reverse the effects VideoInit() -- called if VideoInit() fails or 
  21.      * if the application is shutting down the video subsystem. 
  22.      */  
  23.     void (*VideoQuit) (_THIS);  
  24.   
  25.   
  26.     /* * * */  
  27.     /* 
  28.      * Display functions 
  29.      */  
  30.   
  31.   
  32.     /* 
  33.      * Get the bounds of a display 
  34.      */  
  35.     int (*GetDisplayBounds) (_THIS, SDL_VideoDisplay * display, SDL_Rect * rect);  
  36.   
  37.   
  38.     /* 
  39.      * Get a list of the available display modes for a display. 
  40.      */  
  41.     void (*GetDisplayModes) (_THIS, SDL_VideoDisplay * display);  
  42.   
  43.   
  44.     /* 
  45.      * Setting the display mode is independent of creating windows, so 
  46.      * when the display mode is changed, all existing windows should have 
  47.      * their data updated accordingly, including the display surfaces 
  48.      * associated with them. 
  49.      */  
  50.     int (*SetDisplayMode) (_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode);  
  51.   
  52.   
  53.     /* * * */  
  54.     /* 
  55.      * Window functions 
  56.      */  
  57.     int (*CreateWindow) (_THIS, SDL_Window * window);  
  58.     int (*CreateWindowFrom) (_THIS, SDL_Window * window, const void *data);  
  59.     void (*SetWindowTitle) (_THIS, SDL_Window * window);  
  60.     void (*SetWindowIcon) (_THIS, SDL_Window * window, SDL_Surface * icon);  
  61.     void (*SetWindowPosition) (_THIS, SDL_Window * window);  
  62.     void (*SetWindowSize) (_THIS, SDL_Window * window);  
  63.     void (*SetWindowMinimumSize) (_THIS, SDL_Window * window);  
  64.     void (*SetWindowMaximumSize) (_THIS, SDL_Window * window);  
  65.     void (*ShowWindow) (_THIS, SDL_Window * window);  
  66.     void (*HideWindow) (_THIS, SDL_Window * window);  
  67.     void (*RaiseWindow) (_THIS, SDL_Window * window);  
  68.     void (*MaximizeWindow) (_THIS, SDL_Window * window);  
  69.     void (*MinimizeWindow) (_THIS, SDL_Window * window);  
  70.     void (*RestoreWindow) (_THIS, SDL_Window * window);  
  71.     void (*SetWindowBordered) (_THIS, SDL_Window * window, SDL_bool bordered);  
  72.     void (*SetWindowFullscreen) (_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen);  
  73.     int (*SetWindowGammaRamp) (_THIS, SDL_Window * window, const Uint16 * ramp);  
  74.     int (*GetWindowGammaRamp) (_THIS, SDL_Window * window, Uint16 * ramp);  
  75.     void (*SetWindowGrab) (_THIS, SDL_Window * window, SDL_bool grabbed);  
  76.     void (*DestroyWindow) (_THIS, SDL_Window * window);  
  77.     int (*CreateWindowFramebuffer) (_THIS, SDL_Window * window, Uint32 * format, void ** pixels, int *pitch);  
  78.     int (*UpdateWindowFramebuffer) (_THIS, SDL_Window * window, const SDL_Rect * rects, int numrects);  
  79.     void (*DestroyWindowFramebuffer) (_THIS, SDL_Window * window);  
  80.     void (*OnWindowEnter) (_THIS, SDL_Window * window);  
  81.   
  82.   
  83.     /* * * */  
  84.     /* 
  85.      * Shaped-window functions 
  86.      */  
  87.     SDL_ShapeDriver shape_driver;  
  88.   
  89.   
  90.     /* Get some platform dependent window information */  
  91.       SDL_bool(*GetWindowWMInfo) (_THIS, SDL_Window * window,  
  92.                                   struct SDL_SysWMinfo * info);  
  93.   
  94.   
  95.     /* * * */  
  96.     /* 
  97.      * OpenGL support 
  98.      */  
  99.     int (*GL_LoadLibrary) (_THIS, const char *path);  
  100.     void *(*GL_GetProcAddress) (_THIS, const char *proc);  
  101.     void (*GL_UnloadLibrary) (_THIS);  
  102.       SDL_GLContext(*GL_CreateContext) (_THIS, SDL_Window * window);  
  103.     int (*GL_MakeCurrent) (_THIS, SDL_Window * window, SDL_GLContext context);  
  104.     void (*GL_GetDrawableSize) (_THIS, SDL_Window * window, int *w, int *h);  
  105.     int (*GL_SetSwapInterval) (_THIS, int interval);  
  106.     int (*GL_GetSwapInterval) (_THIS);  
  107.     void (*GL_SwapWindow) (_THIS, SDL_Window * window);  
  108.     void (*GL_DeleteContext) (_THIS, SDL_GLContext context);  
  109.   
  110.   
  111.     /* * * */  
  112.     /* 
  113.      * Event manager functions 
  114.      */  
  115.     void (*PumpEvents) (_THIS);  
  116.   
  117.   
  118.     /* Suspend the screensaver */  
  119.     void (*SuspendScreenSaver) (_THIS);  
  120.   
  121.   
  122.     /* Text input */  
  123.     void (*StartTextInput) (_THIS);  
  124.     void (*StopTextInput) (_THIS);  
  125.     void (*SetTextInputRect) (_THIS, SDL_Rect *rect);  
  126.   
  127.   
  128.     /* Screen keyboard */  
  129.     SDL_bool (*HasScreenKeyboardSupport) (_THIS);  
  130.     void (*ShowScreenKeyboard) (_THIS, SDL_Window *window);  
  131.     void (*HideScreenKeyboard) (_THIS, SDL_Window *window);  
  132.     SDL_bool (*IsScreenKeyboardShown) (_THIS, SDL_Window *window);  
  133.   
  134.   
  135.     /* Clipboard */  
  136.     int (*SetClipboardText) (_THIS, const char *text);  
  137.     char * (*GetClipboardText) (_THIS);  
  138.     SDL_bool (*HasClipboardText) (_THIS);  
  139.   
  140.   
  141.     /* MessageBox */  
  142.     int (*ShowMessageBox) (_THIS, const SDL_MessageBoxData *messageboxdata, int *buttonid);  
  143.   
  144.   
  145.     /* * * */  
  146.     /* Data common to all drivers */  
  147.     SDL_bool suspend_screensaver;  
  148.     int num_displays;  
  149.     SDL_VideoDisplay *displays;  
  150.     SDL_Window *windows;  
  151.     Uint8 window_magic;  
  152.     Uint32 next_object_id;  
  153.     char * clipboard_text;  
  154.   
  155.   
  156.     /* * * */  
  157.     /* Data used by the GL drivers */  
  158.     struct  
  159.     {  
  160.         int red_size;  
  161.         int green_size;  
  162.         int blue_size;  
  163.         int alpha_size;  
  164.         int depth_size;  
  165.         int buffer_size;  
  166.         int stencil_size;  
  167.         int double_buffer;  
  168.         int accum_red_size;  
  169.         int accum_green_size;  
  170.         int accum_blue_size;  
  171.         int accum_alpha_size;  
  172.         int stereo;  
  173.         int multisamplebuffers;  
  174.         int multisamplesamples;  
  175.         int accelerated;  
  176.         int major_version;  
  177.         int minor_version;  
  178.         int flags;  
  179.         int profile_mask;  
  180.         int share_with_current_context;  
  181.         int framebuffer_srgb_capable;  
  182.         int retained_backing;  
  183.         int driver_loaded;  
  184.         char driver_path[256];  
  185.         void *dll_handle;  
  186.     } gl_config;  
  187.   
  188.   
  189.     /* * * */  
  190.     /* Cache current GL context; don't call the OS when it hasn't changed. */  
  191.     /* We have the global pointers here so Cocoa continues to work the way 
  192.        it always has, and the thread-local storage for the general case. 
  193.      */  
  194.     SDL_Window *current_glwin;  
  195.     SDL_GLContext current_glctx;  
  196.     SDL_TLSID current_glwin_tls;  
  197.     SDL_TLSID current_glctx_tls;  
  198.   
  199.   
  200.     /* * * */  
  201.     /* Data private to this driver */  
  202.     void *driverdata;  
  203.     struct SDL_GLDriverData *gl_data;  
  204.       
  205. #if SDL_VIDEO_OPENGL_EGL  
  206.     struct SDL_EGL_VideoData *egl_data;  
  207. #endif  
  208.       
  209. #if SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2  
  210.     struct SDL_PrivateGLESData *gles_data;  
  211. #endif  
  212.   
  213.   
  214.     /* * * */  
  215.     /* The function used to dispose of this structure */  
  216.     void (*free) (_THIS);  
  217. };  
struct SDL_VideoDevice
{
    /* * * */
    /* The name of this video driver */
    const char *name;


    /* * * */
    /* Initialization/Query functions */


    /*
     * Initialize the native video subsystem, filling in the list of
     * displays for this driver, returning 0 or -1 if there's an error.
     */
    int (*VideoInit) (_THIS);


    /*
     * Reverse the effects VideoInit() -- called if VideoInit() fails or
     * if the application is shutting down the video subsystem.
     */
    void (*VideoQuit) (_THIS);


    /* * * */
    /*
     * Display functions
     */


    /*
     * Get the bounds of a display
     */
    int (*GetDisplayBounds) (_THIS, SDL_VideoDisplay * display, SDL_Rect * rect);


    /*
     * Get a list of the available display modes for a display.
     */
    void (*GetDisplayModes) (_THIS, SDL_VideoDisplay * display);


    /*
     * Setting the display mode is independent of creating windows, so
     * when the display mode is changed, all existing windows should have
     * their data updated accordingly, including the display surfaces
     * associated with them.
     */
    int (*SetDisplayMode) (_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode);


    /* * * */
    /*
     * Window functions
     */
    int (*CreateWindow) (_THIS, SDL_Window * window);
    int (*CreateWindowFrom) (_THIS, SDL_Window * window, const void *data);
    void (*SetWindowTitle) (_THIS, SDL_Window * window);
    void (*SetWindowIcon) (_THIS, SDL_Window * window, SDL_Surface * icon);
    void (*SetWindowPosition) (_THIS, SDL_Window * window);
    void (*SetWindowSize) (_THIS, SDL_Window * window);
    void (*SetWindowMinimumSize) (_THIS, SDL_Window * window);
    void (*SetWindowMaximumSize) (_THIS, SDL_Window * window);
    void (*ShowWindow) (_THIS, SDL_Window * window);
    void (*HideWindow) (_THIS, SDL_Window * window);
    void (*RaiseWindow) (_THIS, SDL_Window * window);
    void (*MaximizeWindow) (_THIS, SDL_Window * window);
    void (*MinimizeWindow) (_THIS, SDL_Window * window);
    void (*RestoreWindow) (_THIS, SDL_Window * window);
    void (*SetWindowBordered) (_THIS, SDL_Window * window, SDL_bool bordered);
    void (*SetWindowFullscreen) (_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen);
    int (*SetWindowGammaRamp) (_THIS, SDL_Window * window, const Uint16 * ramp);
    int (*GetWindowGammaRamp) (_THIS, SDL_Window * window, Uint16 * ramp);
    void (*SetWindowGrab) (_THIS, SDL_Window * window, SDL_bool grabbed);
    void (*DestroyWindow) (_THIS, SDL_Window * window);
    int (*CreateWindowFramebuffer) (_THIS, SDL_Window * window, Uint32 * format, void ** pixels, int *pitch);
    int (*UpdateWindowFramebuffer) (_THIS, SDL_Window * window, const SDL_Rect * rects, int numrects);
    void (*DestroyWindowFramebuffer) (_THIS, SDL_Window * window);
    void (*OnWindowEnter) (_THIS, SDL_Window * window);


    /* * * */
    /*
     * Shaped-window functions
     */
    SDL_ShapeDriver shape_driver;


    /* Get some platform dependent window information */
      SDL_bool(*GetWindowWMInfo) (_THIS, SDL_Window * window,
                                  struct SDL_SysWMinfo * info);


    /* * * */
    /*
     * OpenGL support
     */
    int (*GL_LoadLibrary) (_THIS, const char *path);
    void *(*GL_GetProcAddress) (_THIS, const char *proc);
    void (*GL_UnloadLibrary) (_THIS);
      SDL_GLContext(*GL_CreateContext) (_THIS, SDL_Window * window);
    int (*GL_MakeCurrent) (_THIS, SDL_Window * window, SDL_GLContext context);
    void (*GL_GetDrawableSize) (_THIS, SDL_Window * window, int *w, int *h);
    int (*GL_SetSwapInterval) (_THIS, int interval);
    int (*GL_GetSwapInterval) (_THIS);
    void (*GL_SwapWindow) (_THIS, SDL_Window * window);
    void (*GL_DeleteContext) (_THIS, SDL_GLContext context);


    /* * * */
    /*
     * Event manager functions
     */
    void (*PumpEvents) (_THIS);


    /* Suspend the screensaver */
    void (*SuspendScreenSaver) (_THIS);


    /* Text input */
    void (*StartTextInput) (_THIS);
    void (*StopTextInput) (_THIS);
    void (*SetTextInputRect) (_THIS, SDL_Rect *rect);


    /* Screen keyboard */
    SDL_bool (*HasScreenKeyboardSupport) (_THIS);
    void (*ShowScreenKeyboard) (_THIS, SDL_Window *window);
    void (*HideScreenKeyboard) (_THIS, SDL_Window *window);
    SDL_bool (*IsScreenKeyboardShown) (_THIS, SDL_Window *window);


    /* Clipboard */
    int (*SetClipboardText) (_THIS, const char *text);
    char * (*GetClipboardText) (_THIS);
    SDL_bool (*HasClipboardText) (_THIS);


    /* MessageBox */
    int (*ShowMessageBox) (_THIS, const SDL_MessageBoxData *messageboxdata, int *buttonid);


    /* * * */
    /* Data common to all drivers */
    SDL_bool suspend_screensaver;
    int num_displays;
    SDL_VideoDisplay *displays;
    SDL_Window *windows;
    Uint8 window_magic;
    Uint32 next_object_id;
    char * clipboard_text;


    /* * * */
    /* Data used by the GL drivers */
    struct
    {
        int red_size;
        int green_size;
        int blue_size;
        int alpha_size;
        int depth_size;
        int buffer_size;
        int stencil_size;
        int double_buffer;
        int accum_red_size;
        int accum_green_size;
        int accum_blue_size;
        int accum_alpha_size;
        int stereo;
        int multisamplebuffers;
        int multisamplesamples;
        int accelerated;
        int major_version;
        int minor_version;
        int flags;
        int profile_mask;
        int share_with_current_context;
        int framebuffer_srgb_capable;
        int retained_backing;
        int driver_loaded;
        char driver_path[256];
        void *dll_handle;
    } gl_config;


    /* * * */
    /* Cache current GL context; don't call the OS when it hasn't changed. */
    /* We have the global pointers here so Cocoa continues to work the way
       it always has, and the thread-local storage for the general case.
     */
    SDL_Window *current_glwin;
    SDL_GLContext current_glctx;
    SDL_TLSID current_glwin_tls;
    SDL_TLSID current_glctx_tls;


    /* * * */
    /* Data private to this driver */
    void *driverdata;
    struct SDL_GLDriverData *gl_data;
    
#if SDL_VIDEO_OPENGL_EGL
    struct SDL_EGL_VideoData *egl_data;
#endif
    
#if SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
    struct SDL_PrivateGLESData *gles_data;
#endif


    /* * * */
    /* The function used to dispose of this structure */
    void (*free) (_THIS);
};

这个结构体包含了一大堆的函数指针。这些指针在前文所说的VideoBootStrap的create()方法调用的时候会被赋值。SDL通过调用这些函数指针,完成视频显示的各项内容。


3. 调用选中的SDL_VideoDevice的VideoInit()方法。
选择了合适的SDL_VideoDevice之后,调用该SDL_VideoDevice的VideoInit()就可以真正的初始化视频驱动了。以Windows系统为例。从前文的函数中可以看出,Windows系统的VideoInit()接口实际上调用了WIN_VideoInit()函数。我们来看一下WIN_VideoInit()函数的定义(位于video\windows\SDL_windowsvideo.c)。
[cpp] view plain copy
print ?
  1. int WIN_VideoInit(_THIS)  
  2. {  
  3.     if (WIN_InitModes(_this) < 0) {  
  4.         return -1;  
  5.     }  
  6.   
  7.   
  8.     WIN_InitKeyboard(_this);  
  9.     WIN_InitMouse(_this);  
  10.   
  11.   
  12.     return 0;  
  13. }  
int WIN_VideoInit(_THIS)
{
    if (WIN_InitModes(_this) < 0) {
        return -1;
    }


    WIN_InitKeyboard(_this);
    WIN_InitMouse(_this);


    return 0;
}


其中有3个函数:WIN_InitModes(),WIN_InitKeyboard(),WIN_InitMouse()。后两个函数用于初始化键盘和鼠标,我们暂且不研究。看一下WIN_InitModes()的函数。
[cpp] view plain copy
print ?
  1. int WIN_InitModes(_THIS)  
  2. {  
  3.     int pass;  
  4.     DWORD i, j, count;  
  5.     DISPLAY_DEVICE device;  
  6.   
  7.   
  8.     device.cb = sizeof(device);  
  9.   
  10.   
  11.     /* Get the primary display in the first pass */  
  12.     for (pass = 0; pass < 2; ++pass) {  
  13.         for (i = 0; ; ++i) {  
  14.             TCHAR DeviceName[32];  
  15.   
  16.   
  17.             if (!EnumDisplayDevices(NULL, i, &device, 0)) {  
  18.                 break;  
  19.             }  
  20.             if (!(device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) {  
  21.                 continue;  
  22.             }  
  23.             if (pass == 0) {  
  24.                 if (!(device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)) {  
  25.                     continue;  
  26.                 }  
  27.             } else {  
  28.                 if (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {  
  29.                     continue;  
  30.                 }  
  31.             }  
  32.             SDL_memcpy(DeviceName, device.DeviceName, sizeof(DeviceName));  
  33. #ifdef DEBUG_MODES  
  34.             printf("Device: %s\n", WIN_StringToUTF8(DeviceName));  
  35. #endif  
  36.             count = 0;  
  37.             for (j = 0; ; ++j) {  
  38.                 if (!EnumDisplayDevices(DeviceName, j, &device, 0)) {  
  39.                     break;  
  40.                 }  
  41.                 if (!(device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) {  
  42.                     continue;  
  43.                 }  
  44.                 if (pass == 0) {  
  45.                     if (!(device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)) {  
  46.                         continue;  
  47.                     }  
  48.                 } else {  
  49.                     if (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {  
  50.                         continue;  
  51.                     }  
  52.                 }  
  53.                 count += WIN_AddDisplay(device.DeviceName);  
  54.             }  
  55.             if (count == 0) {  
  56.                 WIN_AddDisplay(DeviceName);  
  57.             }  
  58.         }  
  59.     }  
  60.     if (_this->num_displays == 0) {  
  61.         return SDL_SetError("No displays available");  
  62.     }  
  63.     return 0;  
  64. }  
int WIN_InitModes(_THIS)
{
    int pass;
    DWORD i, j, count;
    DISPLAY_DEVICE device;


    device.cb = sizeof(device);


    /* Get the primary display in the first pass */
    for (pass = 0; pass < 2; ++pass) {
        for (i = 0; ; ++i) {
            TCHAR DeviceName[32];


            if (!EnumDisplayDevices(NULL, i, &device, 0)) {
                break;
            }
            if (!(device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) {
                continue;
            }
            if (pass == 0) {
                if (!(device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)) {
                    continue;
                }
            } else {
                if (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
                    continue;
                }
            }
            SDL_memcpy(DeviceName, device.DeviceName, sizeof(DeviceName));
#ifdef DEBUG_MODES
            printf("Device: %s\n", WIN_StringToUTF8(DeviceName));
#endif
            count = 0;
            for (j = 0; ; ++j) {
                if (!EnumDisplayDevices(DeviceName, j, &device, 0)) {
                    break;
                }
                if (!(device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) {
                    continue;
                }
                if (pass == 0) {
                    if (!(device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)) {
                        continue;
                    }
                } else {
                    if (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
                        continue;
                    }
                }
                count += WIN_AddDisplay(device.DeviceName);
            }
            if (count == 0) {
                WIN_AddDisplay(DeviceName);
            }
        }
    }
    if (_this->num_displays == 0) {
        return SDL_SetError("No displays available");
    }
    return 0;
}


该函数的作用就是获得系统中显示设备的信息。目前还没有深入研究,待有时间再补上该函数的分析。

2.AUDIO(音频)

音频子系统的初始化函数是SDL_ AudioInit ()。它的源代码位于audio\SDL_audio.c文件中,如下所示。

[cpp] view plain copy
print ?
  1. int SDL_AudioInit(const char *driver_name)  
  2. {  
  3.     int i = 0;  
  4.     int initialized = 0;  
  5.     int tried_to_init = 0;  
  6.   
  7.   
  8.     if (SDL_WasInit(SDL_INIT_AUDIO)) {  
  9.         SDL_AudioQuit();        /* shutdown driver if already running. */  
  10.     }  
  11.   
  12.   
  13.     SDL_memset(¤t_audio, '\0'sizeof(current_audio));  
  14.     SDL_memset(open_devices, '\0'sizeof(open_devices));  
  15.   
  16.   
  17.     /* Select the proper audio driver */  
  18.     if (driver_name == NULL) {  
  19.         driver_name = SDL_getenv("SDL_AUDIODRIVER");  
  20.     }  
  21.   
  22.   
  23.     for (i = 0; (!initialized) && (bootstrap[i]); ++i) {  
  24.         /* make sure we should even try this driver before doing so... */  
  25.         const AudioBootStrap *backend = bootstrap[i];  
  26.         if ((driver_name && (SDL_strncasecmp(backend->name, driver_name, SDL_strlen(driver_name)) != 0)) ||  
  27.             (!driver_name && backend->demand_only)) {  
  28.             continue;  
  29.         }  
  30.   
  31.   
  32.         tried_to_init = 1;  
  33.         SDL_memset(¤t_audio, 0, sizeof(current_audio));  
  34.         current_audio.name = backend->name;  
  35.         current_audio.desc = backend->desc;  
  36.         initialized = backend->init(¤t_audio.impl);  
  37.     }  
  38.   
  39.   
  40.     if (!initialized) {  
  41.         /* specific drivers will set the error message if they fail... */  
  42.         if (!tried_to_init) {  
  43.             if (driver_name) {  
  44.                 SDL_SetError("Audio target '%s' not available", driver_name);  
  45.             } else {  
  46.                 SDL_SetError("No available audio device");  
  47.             }  
  48.         }  
  49.   
  50.   
  51.         SDL_memset(¤t_audio, 0, sizeof(current_audio));  
  52.         return (-1);            /* No driver was available, so fail. */  
  53.     }  
  54.   
  55.   
  56.     finalize_audio_entry_points();  
  57.   
  58.   
  59.     return (0);  
  60. }  
int SDL_AudioInit(const char *driver_name)
{
    int i = 0;
    int initialized = 0;
    int tried_to_init = 0;


    if (SDL_WasInit(SDL_INIT_AUDIO)) {
        SDL_AudioQuit();        /* shutdown driver if already running. */
    }


    SDL_memset(¤t_audio, '\0', sizeof(current_audio));
    SDL_memset(open_devices, '\0', sizeof(open_devices));


    /* Select the proper audio driver */
    if (driver_name == NULL) {
        driver_name = SDL_getenv("SDL_AUDIODRIVER");
    }


    for (i = 0; (!initialized) && (bootstrap[i]); ++i) {
        /* make sure we should even try this driver before doing so... */
        const AudioBootStrap *backend = bootstrap[i];
        if ((driver_name && (SDL_strncasecmp(backend->name, driver_name, SDL_strlen(driver_name)) != 0)) ||
            (!driver_name && backend->demand_only)) {
            continue;
        }


        tried_to_init = 1;
        SDL_memset(¤t_audio, 0, sizeof(current_audio));
        current_audio.name = backend->name;
        current_audio.desc = backend->desc;
        initialized = backend->init(¤t_audio.impl);
    }


    if (!initialized) {
        /* specific drivers will set the error message if they fail... */
        if (!tried_to_init) {
            if (driver_name) {
                SDL_SetError("Audio target '%s' not available", driver_name);
            } else {
                SDL_SetError("No available audio device");
            }
        }


        SDL_memset(¤t_audio, 0, sizeof(current_audio));
        return (-1);            /* No driver was available, so fail. */
    }


    finalize_audio_entry_points();


    return (0);
}


音频初始化和视频很类似,比视频简单一些,关键在于选择一个合适的SDL_AudioDriver。


在这里,涉及到两个重要的结构体:SDL_AudioDriver以及AudioBootStrap。其中SDL_AudioDriver代表了一个音频驱动程序。AudioBootStrap从字面上理解是“音频驱动程序的引导程序”,即用于创建一个SDL_AudioDriver。可以看出音频子系统中的结构体和视频子系统中的结构体的格式基本上是一模一样的。我们先来看看AudioBootStrap这个结构体。它的定义如下(位于audio\SDL_sysaudio.h)。
[cpp] view plain copy
print ?
  1. typedef struct AudioBootStrap  
  2. {  
  3.     const char *name;  
  4.     const char *desc;  
  5.     int (*init) (SDL_AudioDriverImpl * impl);  
  6.     int demand_only;  /* 1==request explicitly, or it won't be available. */  
  7. } AudioBootStrap;  
typedef struct AudioBootStrap
{
    const char *name;
    const char *desc;
    int (*init) (SDL_AudioDriverImpl * impl);
    int demand_only;  /* 1==request explicitly, or it won't be available. */
} AudioBootStrap;

可以看出它的定义比较简单,每个字段的含义如下:
name:驱动名称
desc:描述
init():创建SDL_AudioDriver的函数指针
demand_only:没有研究过。

SDL中有一个AudioBootStrap类型的静态数组bootstrap。用于存储SDL支持的音频驱动程序。该静态数组定义如下(位于audio\SDL_audio.c)。
[cpp] view plain copy
print ?
  1. /* Available audio drivers */  
  2. static const AudioBootStrap *const bootstrap[] = {  
  3. #if SDL_AUDIO_DRIVER_PULSEAUDIO  
  4.     &PULSEAUDIO_bootstrap,  
  5. #endif  
  6. #if SDL_AUDIO_DRIVER_ALSA  
  7.     &ALSA_bootstrap,  
  8. #endif  
  9. #if SDL_AUDIO_DRIVER_SNDIO  
  10.     &SNDIO_bootstrap,  
  11. #endif  
  12. #if SDL_AUDIO_DRIVER_BSD  
  13.     &BSD_AUDIO_bootstrap,  
  14. #endif  
  15. #if SDL_AUDIO_DRIVER_OSS  
  16.     &DSP_bootstrap,  
  17. #endif  
  18. #if SDL_AUDIO_DRIVER_QSA  
  19.     &QSAAUDIO_bootstrap,  
  20. #endif  
  21. #if SDL_AUDIO_DRIVER_SUNAUDIO  
  22.     &SUNAUDIO_bootstrap,  
  23. #endif  
  24. #if SDL_AUDIO_DRIVER_ARTS  
  25.     &ARTS_bootstrap,  
  26. #endif  
  27. #if SDL_AUDIO_DRIVER_ESD  
  28.     &ESD_bootstrap,  
  29. #endif  
  30. #if SDL_AUDIO_DRIVER_NAS  
  31.     &NAS_bootstrap,  
  32. #endif  
  33. #if SDL_AUDIO_DRIVER_XAUDIO2  
  34.     &XAUDIO2_bootstrap,  
  35. #endif  
  36. #if SDL_AUDIO_DRIVER_DSOUND  
  37.     &DSOUND_bootstrap,  
  38. #endif  
  39. #if SDL_AUDIO_DRIVER_WINMM  
  40.     &WINMM_bootstrap,  
  41. #endif  
  42. #if SDL_AUDIO_DRIVER_PAUDIO  
  43.     &PAUDIO_bootstrap,  
  44. #endif  
  45. #if SDL_AUDIO_DRIVER_HAIKU  
  46.     &HAIKUAUDIO_bootstrap,  
  47. #endif  
  48. #if SDL_AUDIO_DRIVER_COREAUDIO  
  49.     &COREAUDIO_bootstrap,  
  50. #endif  
  51. #if SDL_AUDIO_DRIVER_DISK  
  52.     &DISKAUD_bootstrap,  
  53. #endif  
  54. #if SDL_AUDIO_DRIVER_DUMMY  
  55.     &DUMMYAUD_bootstrap,  
  56. #endif  
  57. #if SDL_AUDIO_DRIVER_FUSIONSOUND  
  58.     &FUSIONSOUND_bootstrap,  
  59. #endif  
  60. #if SDL_AUDIO_DRIVER_ANDROID  
  61.     &ANDROIDAUD_bootstrap,  
  62. #endif  
  63. #if SDL_AUDIO_DRIVER_PSP  
  64.     &PSPAUD_bootstrap,  
  65. #endif  
  66.     NULL  
  67. };  
/* Available audio drivers */
static const AudioBootStrap *const bootstrap[] = {
#if SDL_AUDIO_DRIVER_PULSEAUDIO
    &PULSEAUDIO_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_ALSA
    &ALSA_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_SNDIO
    &SNDIO_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_BSD
    &BSD_AUDIO_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_OSS
    &DSP_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_QSA
    &QSAAUDIO_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_SUNAUDIO
    &SUNAUDIO_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_ARTS
    &ARTS_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_ESD
    &ESD_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_NAS
    &NAS_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_XAUDIO2
    &XAUDIO2_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_DSOUND
    &DSOUND_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_WINMM
    &WINMM_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_PAUDIO
    &PAUDIO_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_HAIKU
    &HAIKUAUDIO_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_COREAUDIO
    &COREAUDIO_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_DISK
    &DISKAUD_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_DUMMY
    &DUMMYAUD_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_FUSIONSOUND
    &FUSIONSOUND_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_ANDROID
    &ANDROIDAUD_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_PSP
    &PSPAUD_bootstrap,
#endif
    NULL
};


在这里我们可以看一下DirectSound的AudioBootStrap的变量DSOUND_bootstrap(audio\directsound\SDL_directsound.c)。
[cpp] view plain copy
print ?
  1. AudioBootStrap DSOUND_bootstrap = {  
  2.     "directsound""DirectSound", DSOUND_Init, 0  
  3. };  
AudioBootStrap DSOUND_bootstrap = {
    "directsound", "DirectSound", DSOUND_Init, 0
};

可以看出该音频驱动名称为“directsound”,描述为“DirectSound”,创建SDL_AudioDriver的函数为“DSOUND_Init()”。
下面看一下DirectSound初始化函数DSOUND_Init()的定义。
[cpp] view plain copy
print ?
  1. static int DSOUND_Init(SDL_AudioDriverImpl * impl)  
  2. {  
  3.     if (!DSOUND_Load()) {  
  4.         return 0;  
  5.     }  
  6.   
  7.   
  8.     /* Set the function pointers */  
  9.     impl->DetectDevices = DSOUND_DetectDevices;  
  10.     impl->OpenDevice = DSOUND_OpenDevice;  
  11.     impl->PlayDevice = DSOUND_PlayDevice;  
  12.     impl->WaitDevice = DSOUND_WaitDevice;  
  13.     impl->WaitDone = DSOUND_WaitDone;  
  14.     impl->GetDeviceBuf = DSOUND_GetDeviceBuf;  
  15.     impl->CloseDevice = DSOUND_CloseDevice;  
  16.     impl->Deinitialize = DSOUND_Deinitialize;  
  17.   
  18.   
  19.     return 1;   /* this audio target is available. */  
  20. }  
static int DSOUND_Init(SDL_AudioDriverImpl * impl)
{
    if (!DSOUND_Load()) {
        return 0;
    }


    /* Set the function pointers */
    impl->DetectDevices = DSOUND_DetectDevices;
    impl->OpenDevice = DSOUND_OpenDevice;
    impl->PlayDevice = DSOUND_PlayDevice;
    impl->WaitDevice = DSOUND_WaitDevice;
    impl->WaitDone = DSOUND_WaitDone;
    impl->GetDeviceBuf = DSOUND_GetDeviceBuf;
    impl->CloseDevice = DSOUND_CloseDevice;
    impl->Deinitialize = DSOUND_Deinitialize;


    return 1;   /* this audio target is available. */
}


和视频驱动的初始化一样,音频驱动初始化也是对SDL_AudioDriver的接口指针进行赋值。在这里涉及到了一个DirectSound的加载函数DSOUND_Load(),我们可以看一下它的代码。
[cpp] view plain copy
print ?
  1. static int DSOUND_Load(void)  
  2. {  
  3.     int loaded = 0;  
  4.   
  5.   
  6.     DSOUND_Unload();  
  7.   
  8.   
  9.     DSoundDLL = SDL_LoadObject("DSOUND.DLL");  
  10.     if (DSoundDLL == NULL) {  
  11.         SDL_SetError("DirectSound: failed to load DSOUND.DLL");  
  12.     } else {  
  13.         /* Now make sure we have DirectX 8 or better... */  
  14.         #define DSOUNDLOAD(f) { \  
  15.             p##f = (fn##f) SDL_LoadFunction(DSoundDLL, #f); \  
  16.             if (!p##f) loaded = 0; \  
  17.         }  
  18.         loaded = 1;  /* will reset if necessary. */  
  19.         DSOUNDLOAD(DirectSoundCreate8);  
  20.         DSOUNDLOAD(DirectSoundEnumerateW);  
  21.         DSOUNDLOAD(DirectSoundCaptureEnumerateW);  
  22.         #undef DSOUNDLOAD  
  23.   
  24.   
  25.         if (!loaded) {  
  26.             SDL_SetError("DirectSound: System doesn't appear to have DX8.");  
  27.         }  
  28.     }  
  29.   
  30.   
  31.     if (!loaded) {  
  32.         DSOUND_Unload();  
  33.     }  
  34.   
  35.   
  36.     return loaded;  
  37. }  
static int DSOUND_Load(void)
{
    int loaded = 0;


    DSOUND_Unload();


    DSoundDLL = SDL_LoadObject("DSOUND.DLL");
    if (DSoundDLL == NULL) {
        SDL_SetError("DirectSound: failed to load DSOUND.DLL");
    } else {
        /* Now make sure we have DirectX 8 or better... */
        #define DSOUNDLOAD(f) { \
            p##f = (fn##f) SDL_LoadFunction(DSoundDLL, #f); \
            if (!p##f) loaded = 0; \
        }
        loaded = 1;  /* will reset if necessary. */
        DSOUNDLOAD(DirectSoundCreate8);
        DSOUNDLOAD(DirectSoundEnumerateW);
        DSOUNDLOAD(DirectSoundCaptureEnumerateW);
        #undef DSOUNDLOAD


        if (!loaded) {
            SDL_SetError("DirectSound: System doesn't appear to have DX8.");
        }
    }


    if (!loaded) {
        DSOUND_Unload();
    }


    return loaded;
}


从代码中可以看出,该函数加载了“DSOUND.DLL”的DirectSoundCreate8(),DirectSoundEnumerateW(),DirectSoundCaptureEnumerateW()函数。

=====================================================

SDL源代码分析系列文章列表:

SDL2源代码分析1:初始化(SDL_Init())

SDL2源代码分析2:窗口(SDL_Window)

SDL2源代码分析3:渲染器(SDL_Renderer)

SDL2源代码分析4:纹理(SDL_Texture)

SDL2源代码分析5:更新纹理(SDL_UpdateTexture())

SDL2源代码分析6:复制到渲染器(SDL_RenderCopy())

SDL2源代码分析7:显示(SDL_RenderPresent())

SDL2源代码分析8:视频显示总结

=====================================================


打算花一段时间研究一下SDL的内部代码。前面几篇文章《最简单的视音频播放示例1:总述》中记录了视频、音频播放的技术,文中提及了SDL实际上封装了Direct3D,DirectSound这类的底层API。但是SDL究竟是如何封装的呢?这次打算深入其源代码一探究竟,看看它是如何封装这些API的。

SDL2源代码分析1:初始化(SDL_Init())_第8张图片

SDL简介

有关SDL的简介在《最简单的视音频播放示例7:SDL2播放RGB/YUV》以及《最简单的视音频播放示例9:SDL2播放PCM》中已经叙述过了,不再重复。这两篇文章中也提到了一张SDL的原理图,如下所示:

SDL2源代码分析1:初始化(SDL_Init())_第9张图片

从这个图中可以看出,SDL根据系统的不同调用不同的API完成相应的功能。至于它是如何实现的,将会在后文中详细叙述。下面不再罗嗦,直接进入正题。
使用SDL播放一个视频代码流程大体如下
初始化: 
SDL_Init(): 初始化SDL。 
SDL_CreateWindow(): 创建窗口(Window)。 
SDL_CreateRenderer(): 基于窗口创建渲染器(Render)。 
SDL_CreateTexture(): 创建纹理(Texture)。 
循环渲染数据: 
SDL_UpdateTexture(): 设置纹理的数据。 
SDL_RenderCopy(): 纹理复制给渲染器。 
SDL_RenderPresent(): 显示。
本文分析这个流程中最基本的一个函数SDL_Init()。SDL_Init()是SDL运行的初始,通过分析该函数,可以了解到SDL内部的架构。


获取源代码

SDL的源代码获取十分简单。访问SDL的官方网站(http://www.libsdl.org/),单击左侧的“Download”进入下载页面,然后下载“SourceCode”栏目下的文件就可以了。

SDL2源代码分析1:初始化(SDL_Init())_第10张图片

下载下来的文件只有4MB左右大小,但是解压缩之后竟然有50MB左右大小,确实不可思议。

解压缩之后,源代码目录如下图所示。

SDL2源代码分析1:初始化(SDL_Init())_第11张图片

几个关键的文件夹如下所示:

1. include:存储SDL的头文件的文件夹。

2. src:存储SDL源代码文件的文件夹。SDL根据功能模块的不同,将源代码分成了很多的文件夹。下图中标出了存储SDL几个子系统的源代码的文件夹。

SDL2源代码分析1:初始化(SDL_Init())_第12张图片

3. VisualC:存储VC解决方案的文件夹。从下图中可以看出,包含了VS2008,VS2010,VS2012,VS2013等各个版本的VC的解决方案。

SDL2源代码分析1:初始化(SDL_Init())_第13张图片



实际上从文件名称我们可以看出,其它几个文件夹中,“Xcode,Xcode-iOS”包含了Xcode的项目文件,“test”包含了一些测试例子程序,“android-project”包含了Android下的项目文件。由于我们暂时不研究这些文件,就不详细分析了。

SDL_Init()


函数简介

下面这一部分进入正题,分析SDL的初始化函数SDL_Init()。该函数可以确定希望激活的子系统。SDL_Init()函数原型如下:

[cpp] view plain copy
print ?
  1. int SDLCALL SDL_Init(Uint32 flags)  
int SDLCALL SDL_Init(Uint32 flags)

其中,flags可以取下列值:
SDL_INIT_TIMER:定时器
SDL_INIT_AUDIO:音频
SDL_INIT_VIDEO:视频
SDL_INIT_JOYSTICK:摇杆
SDL_INIT_HAPTIC:触摸屏
SDL_INIT_GAMECONTROLLER:游戏控制器
SDL_INIT_EVENTS:事件
SDL_INIT_NOPARACHUTE:不捕获关键信号(这个不理解)
SDL_INIT_EVERYTHING:包含上述所有选项


函数调用关系图

SDL_Init()关键函数的调用关系可以用下图表示。

  SDL2源代码分析1:初始化(SDL_Init())_第14张图片

上面的函数调用关系图本来是一张高清大图,但是博客不支持这么大尺寸的图片。因此把图片缩小了,看上去比较模糊。相册里面上传了一份原始的大图片:

http://my.csdn.net/leixiaohua1020/album/detail/1792993

选择上述相册里面的图片,右键选择“另存为”即可保存原始大图片。


源代码分析

SDL_Init()的实现位于SDL.c中。定义如下。

[cpp] view plain copy
print ?
  1. int SDL_Init(Uint32 flags)  
  2. {  
  3.     return SDL_InitSubSystem(flags);  
  4. }  
int SDL_Init(Uint32 flags)
{
    return SDL_InitSubSystem(flags);
}

可以看出其代码只有一句,即调用了SDL_InitSubSystem(),下面我们看一下SDL_InitSubSystem()的定义。

[cpp] view plain copy
print ?
  1. int SDL_InitSubSystem(Uint32 flags)  
  2. {  
  3.     if (!SDL_MainIsReady) {  
  4.         SDL_SetError("Application didn't initialize properly, did you include SDL_main.h in the file containing your main() function?");  
  5.         return -1;  
  6.     }  
  7.   
  8.   
  9.     /* Clear the error message */  
  10.     SDL_ClearError();  
  11.   
  12.   
  13. #if SDL_VIDEO_DRIVER_WINDOWS  
  14.     if ((flags & (SDL_INIT_HAPTIC|SDL_INIT_JOYSTICK))) {  
  15.         if (SDL_HelperWindowCreate() < 0) {  
  16.             return -1;  
  17.         }  
  18.     }  
  19. #endif  
  20.   
  21.   
  22. #if !SDL_TIMERS_DISABLED  
  23.     SDL_TicksInit();  
  24. #endif  
  25.   
  26.   
  27.     if ((flags & SDL_INIT_GAMECONTROLLER)) {  
  28.         /* game controller implies joystick */  
  29.         flags |= SDL_INIT_JOYSTICK;  
  30.     }  
  31.   
  32.   
  33.     if ((flags & (SDL_INIT_VIDEO|SDL_INIT_JOYSTICK))) {  
  34.         /* video or joystick implies events */  
  35.         flags |= SDL_INIT_EVENTS;  
  36.     }  
  37.   
  38.   
  39.     /* Initialize the event subsystem */  
  40.     if ((flags & SDL_INIT_EVENTS)) {  
  41. #if !SDL_EVENTS_DISABLED  
  42.         if (SDL_PrivateShouldInitSubsystem(SDL_INIT_EVENTS)) {  
  43.             if (SDL_StartEventLoop() < 0) {  
  44.                 return (-1);  
  45.             }  
  46.             SDL_QuitInit();  
  47.         }  
  48.         SDL_PrivateSubsystemRefCountIncr(SDL_INIT_EVENTS);  
  49. #else  
  50.         return SDL_SetError("SDL not built with events support");  
  51. #endif  
  52.     }  
  53.   
  54.   
  55.     /* Initialize the timer subsystem */  
  56.     if ((flags & SDL_INIT_TIMER)){  
  57. #if !SDL_TIMERS_DISABLED  
  58.         if (SDL_PrivateShouldInitSubsystem(SDL_INIT_TIMER)) {  
  59.             if (SDL_TimerInit() < 0) {  
  60.                 return (-1);  
  61.             }  
  62.         }  
  63.         SDL_PrivateSubsystemRefCountIncr(SDL_INIT_TIMER);  
  64. #else  
  65.         return SDL_SetError("SDL not built with timer support");  
  66. #endif  
  67.     }  
  68.   
  69.   
  70.     /* Initialize the video subsystem */  
  71.     if ((flags & SDL_INIT_VIDEO)){  
  72. #if !SDL_VIDEO_DISABLED  
  73.         if (SDL_PrivateShouldInitSubsystem(SDL_INIT_VIDEO)) {  
  74.             if (SDL_VideoInit(NULL) < 0) {  
  75.                 return (-1);  
  76.             }  
  77.         }  
  78.         SDL_PrivateSubsystemRefCountIncr(SDL_INIT_VIDEO);  
  79. #else  
  80.         return SDL_SetError("SDL not built with video support");  
  81. #endif  
  82.     }  
  83.   
  84.   
  85.     /* Initialize the audio subsystem */  
  86.     if ((flags & SDL_INIT_AUDIO)){  
  87. #if !SDL_AUDIO_DISABLED  
  88.         if (SDL_PrivateShouldInitSubsystem(SDL_INIT_AUDIO)) {  
  89.             if (SDL_AudioInit(NULL) < 0) {  
  90.                 return (-1);  
  91.             }  
  92.         }  
  93.         SDL_PrivateSubsystemRefCountIncr(SDL_INIT_AUDIO);  
  94. #else  
  95.         return SDL_SetError("SDL not built with audio support");  
  96. #endif  
  97.     }  
  98.   
  99.   
  100.     /* Initialize the joystick subsystem */  
  101.     if ((flags & SDL_INIT_JOYSTICK)){  
  102. #if !SDL_JOYSTICK_DISABLED  
  103.         if (SDL_PrivateShouldInitSubsystem(SDL_INIT_JOYSTICK)) {  
  104.            if (SDL_JoystickInit() < 0) {  
  105.                return (-1);  
  106.            }  
  107.         }  
  108.         SDL_PrivateSubsystemRefCountIncr(SDL_INIT_JOYSTICK);  
  109. #else  
  110.         return SDL_SetError("SDL not built with joystick support");  
  111. #endif  
  112.     }  
  113.   
  114.   
  115.     if ((flags & SDL_INIT_GAMECONTROLLER)){  
  116. #if !SDL_JOYSTICK_DISABLED  
  117.         if (SDL_PrivateShouldInitSubsystem(SDL_INIT_GAMECONTROLLER)) {  
  118.             if (SDL_GameControllerInit() < 0) {  
  119.                 return (-1);  
  120.             }  
  121.         }  
  122.         SDL_PrivateSubsystemRefCountIncr(SDL_INIT_GAMECONTROLLER);  
  123. #else  
  124.         return SDL_SetError("SDL not built with joystick support");  
  125. #endif  
  126.     }  
  127.   
  128.   
  129.     /* Initialize the haptic subsystem */  
  130.     if ((flags & SDL_INIT_HAPTIC)){  
  131. #if !SDL_HAPTIC_DISABLED  
  132.         if (SDL_PrivateShouldInitSubsystem(SDL_INIT_HAPTIC)) {  
  133.             if (SDL_HapticInit() < 0) {  
  134.                 return (-1);  
  135.             }  
  136.         }  
  137.         SDL_PrivateSubsystemRefCountIncr(SDL_INIT_HAPTIC);  
  138. #else  
  139.         return SDL_SetError("SDL not built with haptic (force feedback) support");  
  140. #endif  
  141.     }  
  142.   
  143.   
  144.     return (0);  
  145. }  
int SDL_InitSubSystem(Uint32 flags)
{
    if (!SDL_MainIsReady) {
        SDL_SetError("Application didn't initialize properly, did you include SDL_main.h in the file containing your main() function?");
        return -1;
    }


    /* Clear the error message */
    SDL_ClearError();


#if SDL_VIDEO_DRIVER_WINDOWS
	if ((flags & (SDL_INIT_HAPTIC|SDL_INIT_JOYSTICK))) {
		if (SDL_HelperWindowCreate() < 0) {
			return -1;
		}
	}
#endif


#if !SDL_TIMERS_DISABLED
    SDL_TicksInit();
#endif


    if ((flags & SDL_INIT_GAMECONTROLLER)) {
        /* game controller implies joystick */
        flags |= SDL_INIT_JOYSTICK;
    }


    if ((flags & (SDL_INIT_VIDEO|SDL_INIT_JOYSTICK))) {
        /* video or joystick implies events */
        flags |= SDL_INIT_EVENTS;
    }


    /* Initialize the event subsystem */
    if ((flags & SDL_INIT_EVENTS)) {
#if !SDL_EVENTS_DISABLED
        if (SDL_PrivateShouldInitSubsystem(SDL_INIT_EVENTS)) {
            if (SDL_StartEventLoop() < 0) {
                return (-1);
            }
            SDL_QuitInit();
        }
        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_EVENTS);
#else
        return SDL_SetError("SDL not built with events support");
#endif
    }


    /* Initialize the timer subsystem */
    if ((flags & SDL_INIT_TIMER)){
#if !SDL_TIMERS_DISABLED
        if (SDL_PrivateShouldInitSubsystem(SDL_INIT_TIMER)) {
            if (SDL_TimerInit() < 0) {
                return (-1);
            }
        }
        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_TIMER);
#else
        return SDL_SetError("SDL not built with timer support");
#endif
    }


    /* Initialize the video subsystem */
    if ((flags & SDL_INIT_VIDEO)){
#if !SDL_VIDEO_DISABLED
        if (SDL_PrivateShouldInitSubsystem(SDL_INIT_VIDEO)) {
            if (SDL_VideoInit(NULL) < 0) {
                return (-1);
            }
        }
        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_VIDEO);
#else
        return SDL_SetError("SDL not built with video support");
#endif
    }


    /* Initialize the audio subsystem */
    if ((flags & SDL_INIT_AUDIO)){
#if !SDL_AUDIO_DISABLED
        if (SDL_PrivateShouldInitSubsystem(SDL_INIT_AUDIO)) {
            if (SDL_AudioInit(NULL) < 0) {
                return (-1);
            }
        }
        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_AUDIO);
#else
        return SDL_SetError("SDL not built with audio support");
#endif
    }


    /* Initialize the joystick subsystem */
    if ((flags & SDL_INIT_JOYSTICK)){
#if !SDL_JOYSTICK_DISABLED
        if (SDL_PrivateShouldInitSubsystem(SDL_INIT_JOYSTICK)) {
           if (SDL_JoystickInit() < 0) {
               return (-1);
           }
        }
        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_JOYSTICK);
#else
        return SDL_SetError("SDL not built with joystick support");
#endif
    }


    if ((flags & SDL_INIT_GAMECONTROLLER)){
#if !SDL_JOYSTICK_DISABLED
        if (SDL_PrivateShouldInitSubsystem(SDL_INIT_GAMECONTROLLER)) {
            if (SDL_GameControllerInit() < 0) {
                return (-1);
            }
        }
        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_GAMECONTROLLER);
#else
        return SDL_SetError("SDL not built with joystick support");
#endif
    }


    /* Initialize the haptic subsystem */
    if ((flags & SDL_INIT_HAPTIC)){
#if !SDL_HAPTIC_DISABLED
        if (SDL_PrivateShouldInitSubsystem(SDL_INIT_HAPTIC)) {
            if (SDL_HapticInit() < 0) {
                return (-1);
            }
        }
        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_HAPTIC);
#else
        return SDL_SetError("SDL not built with haptic (force feedback) support");
#endif
    }


    return (0);
}

SDL_InitSubSystem()函数的定义看上去很长,实际上却并不复杂。下面简单阐述一下它的一些关键点:

1. 通过将传入的flag与子系统的宏定义(例如SDL_INIT_VIDEO,SDL_INIT_AUDIO等)相与,判断是否需要初始化该子系统。

2. 有很多的预定义的宏,用于判断SDL是否支持这些子系统。例如SDL_EVENTS_DISABLED,SDL_TIMERS_DISABLED,SDL_VIDEO_DISABLED,SDL_AUDIO_DISABLED,SDL_JOYSTICK_DISABLED,SDL_HAPTIC_DISABLED等。这些宏的定义位于SDL_config_minimal.h文件中,如下所示。

[cpp] view plain copy
print ?
  1. /* Enable the dummy audio driver (src/audio/dummy/\*.c) */  
  2. #define SDL_AUDIO_DRIVER_DUMMY  1  
  3.   
  4.   
  5. /* Enable the stub joystick driver (src/joystick/dummy/\*.c) */  
  6. #define SDL_JOYSTICK_DISABLED   1  
  7.   
  8.   
  9. /* Enable the stub haptic driver (src/haptic/dummy/\*.c) */  
  10. #define SDL_HAPTIC_DISABLED 1  
  11.   
  12.   
  13. /* Enable the stub shared object loader (src/loadso/dummy/\*.c) */  
  14. #define SDL_LOADSO_DISABLED 1  
  15.   
  16.   
  17. /* Enable the stub thread support (src/thread/generic/\*.c) */  
  18. #define SDL_THREADS_DISABLED    1  
  19.   
  20.   
  21. /* Enable the stub timer support (src/timer/dummy/\*.c) */  
  22. #define SDL_TIMERS_DISABLED 1  
  23.   
  24.   
  25. /* Enable the dummy video driver (src/video/dummy/\*.c) */  
  26. #define SDL_VIDEO_DRIVER_DUMMY  1  
  27.   
  28.   
  29. /* Enable the dummy filesystem driver (src/filesystem/dummy/\*.c) */  
  30. #define SDL_FILESYSTEM_DUMMY  1  
/* Enable the dummy audio driver (src/audio/dummy/\*.c) */
#define SDL_AUDIO_DRIVER_DUMMY  1


/* Enable the stub joystick driver (src/joystick/dummy/\*.c) */
#define SDL_JOYSTICK_DISABLED   1


/* Enable the stub haptic driver (src/haptic/dummy/\*.c) */
#define SDL_HAPTIC_DISABLED 1


/* Enable the stub shared object loader (src/loadso/dummy/\*.c) */
#define SDL_LOADSO_DISABLED 1


/* Enable the stub thread support (src/thread/generic/\*.c) */
#define SDL_THREADS_DISABLED    1


/* Enable the stub timer support (src/timer/dummy/\*.c) */
#define SDL_TIMERS_DISABLED 1


/* Enable the dummy video driver (src/video/dummy/\*.c) */
#define SDL_VIDEO_DRIVER_DUMMY  1


/* Enable the dummy filesystem driver (src/filesystem/dummy/\*.c) */
#define SDL_FILESYSTEM_DUMMY  1

如果这些定义取值不为0,代表该子系统已经被disable了,就不编译指定子系统的源代码了。初始化的时候会调用SDL_SetError()函数输出错误信息。例如SDL_VIDEO_DISABLED如果设置为1的话,初始化视频子系统的时候会执行以下代码。
[cpp] view plain copy
print ?
  1. SDL_SetError("SDL not built with video support");  
SDL_SetError("SDL not built with video support");

3. 在每一个子系统真正初始化之前,都会调用一个函数SDL_PrivateShouldInitSubsystem()。该函数用于检查目标子系统是否需要初始化。

4. 在一个子系统初始化之后,都会调用一个函数SDL_PrivateSubsystemRefCountIncr()。该函数用于增加子系统的引用计数。

5. 下表列出各个子系统的初始化函数。

子系统名称

函数

AUDIO(音频)

SDL_AudioInit()

VIDEO(视频)

SDL_VideoInit()

TIMERS(定时器)

SDL_TicksInit(),SDL_TimerInit()

EVENTS(事件)

SDL_StartEventLoop()

JOYSTICK(摇杆)

SDL_GameControllerInit()

HAPTIC(触摸屏)

SDL_HapticInit()


我们先不看JOYSTICK(摇杆),HAPTIC(触摸屏)这些方面的代码,专门关注AUDIO(音频),VIDEO(视频)这两个方面的代码。

1.VIDEO(视频)

视频子系统的初始化函数是SDL_VideoInit()。它的源代码位于video\SDL_video.c文件中,如下所示。
[cpp] view plain copy
print ?
  1. /* 
  2.  * Initialize the video and event subsystems -- determine native pixel format 
  3.  */  
  4. int SDL_VideoInit(const char *driver_name)  
  5. {  
  6.     SDL_VideoDevice *video;  
  7.     const char *hint;  
  8.     int index;  
  9.     int i;  
  10.     SDL_bool allow_screensaver;  
  11.   
  12.   
  13.     /* Check to make sure we don't overwrite '_this' */  
  14.     if (_this != NULL) {  
  15.         SDL_VideoQuit();  
  16.     }  
  17.   
  18.   
  19. #if !SDL_TIMERS_DISABLED  
  20.     SDL_TicksInit();  
  21. #endif  
  22.   
  23.   
  24.     /* Start the event loop */  
  25.     if (SDL_InitSubSystem(SDL_INIT_EVENTS) < 0 ||  
  26.         SDL_KeyboardInit() < 0 ||  
  27.         SDL_MouseInit() < 0 ||  
  28.         SDL_TouchInit() < 0) {  
  29.         return -1;  
  30.     }  
  31.   
  32.   
  33.     /* Select the proper video driver */  
  34.     index = 0;  
  35.     video = NULL;  
  36.     if (driver_name == NULL) {  
  37.         driver_name = SDL_getenv("SDL_VIDEODRIVER");  
  38.     }  
  39.     if (driver_name != NULL) {  
  40.         for (i = 0; bootstrap[i]; ++i) {  
  41.             if (SDL_strncasecmp(bootstrap[i]->name, driver_name, SDL_strlen(driver_name)) == 0) {  
  42.                 if (bootstrap[i]->available()) {  
  43.                     video = bootstrap[i]->create(index);  
  44.                     break;  
  45.                 }  
  46.             }  
  47.         }  
  48.     } else {  
  49.         for (i = 0; bootstrap[i]; ++i) {  
  50.             if (bootstrap[i]->available()) {  
  51.                 video = bootstrap[i]->create(index);  
  52.                 if (video != NULL) {  
  53.                     break;  
  54.                 }  
  55.             }  
  56.         }  
  57.     }  
  58.     if (video == NULL) {  
  59.         if (driver_name) {  
  60.             return SDL_SetError("%s not available", driver_name);  
  61.         }  
  62.         return SDL_SetError("No available video device");  
  63.     }  
  64.     _this = video;  
  65.     _this->name = bootstrap[i]->name;  
  66.     _this->next_object_id = 1;  
  67.   
  68.   
  69.   
  70.   
  71.     /* Set some very sane GL defaults */  
  72.     _this->gl_config.driver_loaded = 0;  
  73.     _this->gl_config.dll_handle = NULL;  
  74.     SDL_GL_ResetAttributes();  
  75.   
  76.   
  77.     _this->current_glwin_tls = SDL_TLSCreate();  
  78.     _this->current_glctx_tls = SDL_TLSCreate();  
  79.   
  80.   
  81.     /* Initialize the video subsystem */  
  82.     if (_this->VideoInit(_this) < 0) {  
  83.         SDL_VideoQuit();  
  84.         return -1;  
  85.     }  
  86.   
  87.   
  88.     /* Make sure some displays were added */  
  89.     if (_this->num_displays == 0) {  
  90.         SDL_VideoQuit();  
  91.         return SDL_SetError("The video driver did not add any displays");  
  92.     }  
  93.   
  94.   
  95.     /* Add the renderer framebuffer emulation if desired */  
  96.     if (ShouldUseTextureFramebuffer()) {  
  97.         _this->CreateWindowFramebuffer = SDL_CreateWindowTexture;  
  98.         _this->UpdateWindowFramebuffer = SDL_UpdateWindowTexture;  
  99.         _this->DestroyWindowFramebuffer = SDL_DestroyWindowTexture;  
  100.     }  
  101.   
  102.   
  103.     /* Disable the screen saver by default. This is a change from <= 2.0.1, 
  104.        but most things using SDL are games or media players; you wouldn't 
  105.        want a screensaver to trigger if you're playing exclusively with a 
  106.        joystick, or passively watching a movie. Things that use SDL but 
  107.        function more like a normal desktop app should explicitly reenable the 
  108.        screensaver. */  
  109.     hint = SDL_GetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER);  
  110.     if (hint) {  
  111.         allow_screensaver = SDL_atoi(hint) ? SDL_TRUE : SDL_FALSE;  
  112.     } else {  
  113.         allow_screensaver = SDL_FALSE;  
  114.     }  
  115.     if (!allow_screensaver) {  
  116.         SDL_DisableScreenSaver();  
  117.     }  
  118.   
  119.   
  120.     /* If we don't use a screen keyboard, turn on text input by default, 
  121.        otherwise programs that expect to get text events without enabling 
  122.        UNICODE input won't get any events. 
  123.  
  124.  
  125.        Actually, come to think of it, you needed to call SDL_EnableUNICODE(1) 
  126.        in SDL 1.2 before you got text input events.  Hmm... 
  127.      */  
  128.     if (!SDL_HasScreenKeyboardSupport()) {  
  129.         SDL_StartTextInput();  
  130.     }  
  131.   
  132.   
  133.     /* We're ready to go! */  
  134.     return 0;  
  135. }  
/*
 * Initialize the video and event subsystems -- determine native pixel format
 */
int SDL_VideoInit(const char *driver_name)
{
    SDL_VideoDevice *video;
    const char *hint;
    int index;
    int i;
    SDL_bool allow_screensaver;


    /* Check to make sure we don't overwrite '_this' */
    if (_this != NULL) {
        SDL_VideoQuit();
    }


#if !SDL_TIMERS_DISABLED
    SDL_TicksInit();
#endif


    /* Start the event loop */
    if (SDL_InitSubSystem(SDL_INIT_EVENTS) < 0 ||
        SDL_KeyboardInit() < 0 ||
        SDL_MouseInit() < 0 ||
        SDL_TouchInit() < 0) {
        return -1;
    }


    /* Select the proper video driver */
    index = 0;
    video = NULL;
    if (driver_name == NULL) {
        driver_name = SDL_getenv("SDL_VIDEODRIVER");
    }
    if (driver_name != NULL) {
        for (i = 0; bootstrap[i]; ++i) {
            if (SDL_strncasecmp(bootstrap[i]->name, driver_name, SDL_strlen(driver_name)) == 0) {
                if (bootstrap[i]->available()) {
                    video = bootstrap[i]->create(index);
                    break;
                }
            }
        }
    } else {
        for (i = 0; bootstrap[i]; ++i) {
            if (bootstrap[i]->available()) {
                video = bootstrap[i]->create(index);
                if (video != NULL) {
                    break;
                }
            }
        }
    }
    if (video == NULL) {
        if (driver_name) {
            return SDL_SetError("%s not available", driver_name);
        }
        return SDL_SetError("No available video device");
    }
    _this = video;
    _this->name = bootstrap[i]->name;
    _this->next_object_id = 1;




    /* Set some very sane GL defaults */
    _this->gl_config.driver_loaded = 0;
    _this->gl_config.dll_handle = NULL;
    SDL_GL_ResetAttributes();


    _this->current_glwin_tls = SDL_TLSCreate();
    _this->current_glctx_tls = SDL_TLSCreate();


    /* Initialize the video subsystem */
    if (_this->VideoInit(_this) < 0) {
        SDL_VideoQuit();
        return -1;
    }


    /* Make sure some displays were added */
    if (_this->num_displays == 0) {
        SDL_VideoQuit();
        return SDL_SetError("The video driver did not add any displays");
    }


    /* Add the renderer framebuffer emulation if desired */
    if (ShouldUseTextureFramebuffer()) {
        _this->CreateWindowFramebuffer = SDL_CreateWindowTexture;
        _this->UpdateWindowFramebuffer = SDL_UpdateWindowTexture;
        _this->DestroyWindowFramebuffer = SDL_DestroyWindowTexture;
    }


    /* Disable the screen saver by default. This is a change from <= 2.0.1,
       but most things using SDL are games or media players; you wouldn't
       want a screensaver to trigger if you're playing exclusively with a
       joystick, or passively watching a movie. Things that use SDL but
       function more like a normal desktop app should explicitly reenable the
       screensaver. */
    hint = SDL_GetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER);
    if (hint) {
        allow_screensaver = SDL_atoi(hint) ? SDL_TRUE : SDL_FALSE;
    } else {
        allow_screensaver = SDL_FALSE;
    }
    if (!allow_screensaver) {
        SDL_DisableScreenSaver();
    }


    /* If we don't use a screen keyboard, turn on text input by default,
       otherwise programs that expect to get text events without enabling
       UNICODE input won't get any events.


       Actually, come to think of it, you needed to call SDL_EnableUNICODE(1)
       in SDL 1.2 before you got text input events.  Hmm...
     */
    if (!SDL_HasScreenKeyboardSupport()) {
        SDL_StartTextInput();
    }


    /* We're ready to go! */
    return 0;
}

下面简单阐述一下它的大致步骤:
1. 初始化一些子系统,比如EVENTS(事件)子系统。也就是说,就算在调用SDL_Init()的时候不指定初始化EVENTS子系统,在初始化VIDEO子系统的时候,同样也会初始化EVENTS子系统。
2. 选择一个合适的SDL_VideoDevice。
在这里,涉及到两个重要的结构体:SDL_VideoDevice以及VideoBootStrap。其中SDL_VideoDevice代表了一个视频驱动程序。VideoBootStrap从字面上理解是“视频驱动程序的引导程序”,即用于创建一个SDL_VideoDevice。因此,我们先来看看VideoBootStrap这个结构体。它的定义如下(位于video\SDL_sysvideo.h)。
[cpp] view plain copy
print ?
  1. typedef struct VideoBootStrap  
  2. {  
  3.     const char *name;  
  4.     const char *desc;  
  5.     int (*available) (void);  
  6.     SDL_VideoDevice *(*create) (int devindex);  
  7. } VideoBootStrap;  
typedef struct VideoBootStrap
{
    const char *name;
    const char *desc;
    int (*available) (void);
    SDL_VideoDevice *(*create) (int devindex);
} VideoBootStrap;

可以看出它的定义比较简单,每个字段的含义如下:
name:驱动名称
desc:描述
available():检查是否可用的一个函数指针
create():创建SDL_VideoDevice的函数指针


SDL中有一个VideoBootStrap类型的静态数组bootstrap。用于存储SDL支持的视频驱动程序。注意这是SDL“跨平台”特性中很重要的一部分。该静态数组定义如下(位于video\SDL_video.c)。
[cpp] view plain copy
print ?
  1. /* Available video drivers */  
  2. static VideoBootStrap *bootstrap[] = {  
  3. #if SDL_VIDEO_DRIVER_COCOA  
  4.     &COCOA_bootstrap,  
  5. #endif  
  6. #if SDL_VIDEO_DRIVER_X11  
  7.     &X11_bootstrap,  
  8. #endif  
  9. #if SDL_VIDEO_DRIVER_MIR  
  10.     &MIR_bootstrap,  
  11. #endif  
  12. #if SDL_VIDEO_DRIVER_DIRECTFB  
  13.     &DirectFB_bootstrap,  
  14. #endif  
  15. #if SDL_VIDEO_DRIVER_WINDOWS  
  16.     &WINDOWS_bootstrap,  
  17. #endif  
  18. #if SDL_VIDEO_DRIVER_WINRT  
  19.     &WINRT_bootstrap,  
  20. #endif  
  21. #if SDL_VIDEO_DRIVER_HAIKU  
  22.     &HAIKU_bootstrap,  
  23. #endif  
  24. #if SDL_VIDEO_DRIVER_PANDORA  
  25.     &PND_bootstrap,  
  26. #endif  
  27. #if SDL_VIDEO_DRIVER_UIKIT  
  28.     &UIKIT_bootstrap,  
  29. #endif  
  30. #if SDL_VIDEO_DRIVER_ANDROID  
  31.     &Android_bootstrap,  
  32. #endif  
  33. #if SDL_VIDEO_DRIVER_PSP  
  34.     &PSP_bootstrap,  
  35. #endif  
  36. #if SDL_VIDEO_DRIVER_RPI  
  37.     &RPI_bootstrap,  
  38. #endif   
  39. #if SDL_VIDEO_DRIVER_WAYLAND  
  40.     &Wayland_bootstrap,  
  41. #endif  
  42. #if SDL_VIDEO_DRIVER_DUMMY  
  43.     &DUMMY_bootstrap,  
  44. #endif  
  45.     NULL  
  46. };  
/* Available video drivers */
static VideoBootStrap *bootstrap[] = {
#if SDL_VIDEO_DRIVER_COCOA
    &COCOA_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_X11
    &X11_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_MIR
    &MIR_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_DIRECTFB
    &DirectFB_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_WINDOWS
    &WINDOWS_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_WINRT
    &WINRT_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_HAIKU
    &HAIKU_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_PANDORA
    &PND_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_UIKIT
    &UIKIT_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_ANDROID
    &Android_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_PSP
    &PSP_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_RPI
    &RPI_bootstrap,
#endif 
#if SDL_VIDEO_DRIVER_WAYLAND
    &Wayland_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_DUMMY
    &DUMMY_bootstrap,
#endif
    NULL
};


从代码中可以看出,SDL通过预编译宏取值是否非0来判断是否支持该视频驱动。我们可以看一下Windows的视频设备驱动的定义。该设备驱动同样是一个静态变量,名称为WINDOWS_bootstrap(位于video\windows\SDL_windowsvideo.c)。
[cpp] view plain copy
print ?
  1. VideoBootStrap WINDOWS_bootstrap = {  
  2.     "windows""SDL Windows video driver", WIN_Available, WIN_CreateDevice  
  3. };  
VideoBootStrap WINDOWS_bootstrap = {
    "windows", "SDL Windows video driver", WIN_Available, WIN_CreateDevice
};

可以看出该视频驱动名称为“windows”,描述为“SDL Windows video driver”,检查是否可用的函数为“WIN_Available()”,创建SDL_VideoDevice的函数为“WIN_CreateDevice()”。
同样, Android的视频设备驱动的名称为Android_bootstrap;PSP的视频设备驱动为PSP_bootstrap;X11的视频设备驱动为X11_bootstrap。不再一一例举。
下面看一下Windows视频驱动中那两个函数的定义。WIN_Available()定义如下。
[cpp] view plain copy
print ?
  1. static int WIN_Available(void)  
  2. {  
  3.     return (1);  
  4. }  
static int WIN_Available(void)
{
    return (1);
}

可见该函数没有做任何的处理。WIN_CreateDevice()定义如下。

[cpp] view plain copy
print ?
  1. static SDL_VideoDevice *  
  2. WIN_CreateDevice(int devindex)  
  3. {  
  4.     SDL_VideoDevice *device;  
  5.     SDL_VideoData *data;  
  6.   
  7.   
  8.     SDL_RegisterApp(NULL, 0, NULL);  
  9.   
  10.   
  11.     /* Initialize all variables that we clean on shutdown */  
  12.     device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));  
  13.     if (device) {  
  14.         data = (struct SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));  
  15.     } else {  
  16.         data = NULL;  
  17.     }  
  18.     if (!data) {  
  19.         SDL_free(device);  
  20.         SDL_OutOfMemory();  
  21.         return NULL;  
  22.     }  
  23.     device->driverdata = data;  
  24.   
  25.   
  26.     data->userDLL = SDL_LoadObject("USER32.DLL");  
  27.     if (data->userDLL) {  
  28.         data->CloseTouchInputHandle = (BOOL (WINAPI *)( HTOUCHINPUT )) SDL_LoadFunction(data->userDLL, "CloseTouchInputHandle");  
  29.         data->GetTouchInputInfo = (BOOL (WINAPI *)( HTOUCHINPUT, UINT, PTOUCHINPUT, int )) SDL_LoadFunction(data->userDLL, "GetTouchInputInfo");  
  30.         data->RegisterTouchWindow = (BOOL (WINAPI *)( HWNDULONG )) SDL_LoadFunction(data->userDLL, "RegisterTouchWindow");  
  31.     }  
  32.   
  33.   
  34.     /* Set the function pointers */  
  35.     device->VideoInit = WIN_VideoInit;  
  36.     device->VideoQuit = WIN_VideoQuit;  
  37.     device->GetDisplayBounds = WIN_GetDisplayBounds;  
  38.     device->GetDisplayModes = WIN_GetDisplayModes;  
  39.     device->SetDisplayMode = WIN_SetDisplayMode;  
  40.     device->PumpEvents = WIN_PumpEvents;  
  41.   
  42.   
  43. #undef CreateWindow  
  44.     device->CreateWindow = WIN_CreateWindow;  
  45.     device->CreateWindowFrom = WIN_CreateWindowFrom;  
  46.     device->SetWindowTitle = WIN_SetWindowTitle;  
  47.     device->SetWindowIcon = WIN_SetWindowIcon;  
  48.     device->SetWindowPosition = WIN_SetWindowPosition;  
  49.     device->SetWindowSize = WIN_SetWindowSize;  
  50.     device->ShowWindow = WIN_ShowWindow;  
  51.     device->HideWindow = WIN_HideWindow;  
  52.     device->RaiseWindow = WIN_RaiseWindow;  
  53.     device->MaximizeWindow = WIN_MaximizeWindow;  
  54.     device->MinimizeWindow = WIN_MinimizeWindow;  
  55.     device->RestoreWindow = WIN_RestoreWindow;  
  56.     device->SetWindowBordered = WIN_SetWindowBordered;  
  57.     device->SetWindowFullscreen = WIN_SetWindowFullscreen;  
  58.     device->SetWindowGammaRamp = WIN_SetWindowGammaRamp;  
  59.     device->GetWindowGammaRamp = WIN_GetWindowGammaRamp;  
  60.     device->SetWindowGrab = WIN_SetWindowGrab;  
  61.     device->DestroyWindow = WIN_DestroyWindow;  
  62.     device->GetWindowWMInfo = WIN_GetWindowWMInfo;  
  63.     device->CreateWindowFramebuffer = WIN_CreateWindowFramebuffer;  
  64.     device->UpdateWindowFramebuffer = WIN_UpdateWindowFramebuffer;  
  65.     device->DestroyWindowFramebuffer = WIN_DestroyWindowFramebuffer;  
  66.     device->OnWindowEnter = WIN_OnWindowEnter;  
  67.   
  68.   
  69.     device->shape_driver.CreateShaper = Win32_CreateShaper;  
  70.     device->shape_driver.SetWindowShape = Win32_SetWindowShape;  
  71.     device->shape_driver.ResizeWindowShape = Win32_ResizeWindowShape;  
  72.   
  73.   
  74. #if SDL_VIDEO_OPENGL_WGL  
  75.     device->GL_LoadLibrary = WIN_GL_LoadLibrary;  
  76.     device->GL_GetProcAddress = WIN_GL_GetProcAddress;  
  77.     device->GL_UnloadLibrary = WIN_GL_UnloadLibrary;  
  78.     device->GL_CreateContext = WIN_GL_CreateContext;  
  79.     device->GL_MakeCurrent = WIN_GL_MakeCurrent;  
  80.     device->GL_SetSwapInterval = WIN_GL_SetSwapInterval;  
  81.     device->GL_GetSwapInterval = WIN_GL_GetSwapInterval;  
  82.     device->GL_SwapWindow = WIN_GL_SwapWindow;  
  83.     device->GL_DeleteContext = WIN_GL_DeleteContext;  
  84. #endif  
  85.     device->StartTextInput = WIN_StartTextInput;  
  86.     device->StopTextInput = WIN_StopTextInput;  
  87.     device->SetTextInputRect = WIN_SetTextInputRect;  
  88.   
  89.   
  90.     device->SetClipboardText = WIN_SetClipboardText;  
  91.     device->GetClipboardText = WIN_GetClipboardText;  
  92.     device->HasClipboardText = WIN_HasClipboardText;  
  93.   
  94.   
  95.     device->free = WIN_DeleteDevice;  
  96.   
  97.   
  98.     return device;  
  99. }  
static SDL_VideoDevice *
WIN_CreateDevice(int devindex)
{
    SDL_VideoDevice *device;
    SDL_VideoData *data;


    SDL_RegisterApp(NULL, 0, NULL);


    /* Initialize all variables that we clean on shutdown */
    device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
    if (device) {
        data = (struct SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
    } else {
        data = NULL;
    }
    if (!data) {
        SDL_free(device);
        SDL_OutOfMemory();
        return NULL;
    }
    device->driverdata = data;


    data->userDLL = SDL_LoadObject("USER32.DLL");
    if (data->userDLL) {
        data->CloseTouchInputHandle = (BOOL (WINAPI *)( HTOUCHINPUT )) SDL_LoadFunction(data->userDLL, "CloseTouchInputHandle");
        data->GetTouchInputInfo = (BOOL (WINAPI *)( HTOUCHINPUT, UINT, PTOUCHINPUT, int )) SDL_LoadFunction(data->userDLL, "GetTouchInputInfo");
        data->RegisterTouchWindow = (BOOL (WINAPI *)( HWND, ULONG )) SDL_LoadFunction(data->userDLL, "RegisterTouchWindow");
    }


    /* Set the function pointers */
    device->VideoInit = WIN_VideoInit;
    device->VideoQuit = WIN_VideoQuit;
    device->GetDisplayBounds = WIN_GetDisplayBounds;
    device->GetDisplayModes = WIN_GetDisplayModes;
    device->SetDisplayMode = WIN_SetDisplayMode;
    device->PumpEvents = WIN_PumpEvents;


#undef CreateWindow
    device->CreateWindow = WIN_CreateWindow;
    device->CreateWindowFrom = WIN_CreateWindowFrom;
    device->SetWindowTitle = WIN_SetWindowTitle;
    device->SetWindowIcon = WIN_SetWindowIcon;
    device->SetWindowPosition = WIN_SetWindowPosition;
    device->SetWindowSize = WIN_SetWindowSize;
    device->ShowWindow = WIN_ShowWindow;
    device->HideWindow = WIN_HideWindow;
    device->RaiseWindow = WIN_RaiseWindow;
    device->MaximizeWindow = WIN_MaximizeWindow;
    device->MinimizeWindow = WIN_MinimizeWindow;
    device->RestoreWindow = WIN_RestoreWindow;
    device->SetWindowBordered = WIN_SetWindowBordered;
    device->SetWindowFullscreen = WIN_SetWindowFullscreen;
    device->SetWindowGammaRamp = WIN_SetWindowGammaRamp;
    device->GetWindowGammaRamp = WIN_GetWindowGammaRamp;
    device->SetWindowGrab = WIN_SetWindowGrab;
    device->DestroyWindow = WIN_DestroyWindow;
    device->GetWindowWMInfo = WIN_GetWindowWMInfo;
    device->CreateWindowFramebuffer = WIN_CreateWindowFramebuffer;
    device->UpdateWindowFramebuffer = WIN_UpdateWindowFramebuffer;
    device->DestroyWindowFramebuffer = WIN_DestroyWindowFramebuffer;
    device->OnWindowEnter = WIN_OnWindowEnter;


    device->shape_driver.CreateShaper = Win32_CreateShaper;
    device->shape_driver.SetWindowShape = Win32_SetWindowShape;
    device->shape_driver.ResizeWindowShape = Win32_ResizeWindowShape;


#if SDL_VIDEO_OPENGL_WGL
    device->GL_LoadLibrary = WIN_GL_LoadLibrary;
    device->GL_GetProcAddress = WIN_GL_GetProcAddress;
    device->GL_UnloadLibrary = WIN_GL_UnloadLibrary;
    device->GL_CreateContext = WIN_GL_CreateContext;
    device->GL_MakeCurrent = WIN_GL_MakeCurrent;
    device->GL_SetSwapInterval = WIN_GL_SetSwapInterval;
    device->GL_GetSwapInterval = WIN_GL_GetSwapInterval;
    device->GL_SwapWindow = WIN_GL_SwapWindow;
    device->GL_DeleteContext = WIN_GL_DeleteContext;
#endif
    device->StartTextInput = WIN_StartTextInput;
    device->StopTextInput = WIN_StopTextInput;
    device->SetTextInputRect = WIN_SetTextInputRect;


    device->SetClipboardText = WIN_SetClipboardText;
    device->GetClipboardText = WIN_GetClipboardText;
    device->HasClipboardText = WIN_HasClipboardText;


    device->free = WIN_DeleteDevice;


    return device;
}

该函数首先通过SDL_calloc()的方法为创建的SDL_VideoDevice分配了一块内存,接下来为创建的SDL_VideoDevice结构体中的函数指针赋了一大堆的值。这也是SDL“跨平台”特性的一个特点:通过调用SDL_VideoDevice中的接口函数,就可以调用不同平台的具体实现功能的函数。

PS:在这里补充一个SDL中内存分配函数的知识。在SDL中分配内存使用SDL_malloc(),SDL_calloc(),这些函数实际上就是malloc(),calloc()。它们的定义位于stdlib\SDL_malloc.c文件中。如下所示:

[cpp] view plain copy
print ?
  1. #define memset  SDL_memset  
  2. #define memcpy  SDL_memcpy  
  3. #define malloc  SDL_malloc  
  4. #define calloc  SDL_calloc  
  5. #define realloc SDL_realloc  
  6. #define free    SDL_free  
#define memset  SDL_memset
#define memcpy  SDL_memcpy
#define malloc  SDL_malloc
#define calloc  SDL_calloc
#define realloc SDL_realloc
#define free    SDL_free

下面来看一下SDL_VideoDevice这个结构体的定义(位于video\SDL_sysvideo.h)。
[cpp] view plain copy
print ?
  1. struct SDL_VideoDevice  
  2. {  
  3.     /* * * */  
  4.     /* The name of this video driver */  
  5.     const char *name;  
  6.   
  7.   
  8.     /* * * */  
  9.     /* Initialization/Query functions */  
  10.   
  11.   
  12.     /* 
  13.      * Initialize the native video subsystem, filling in the list of 
  14.      * displays for this driver, returning 0 or -1 if there's an error. 
  15.      */  
  16.     int (*VideoInit) (_THIS);  
  17.   
  18.   
  19.     /* 
  20.      * Reverse the effects VideoInit() -- called if VideoInit() fails or 
  21.      * if the application is shutting down the video subsystem. 
  22.      */  
  23.     void (*VideoQuit) (_THIS);  
  24.   
  25.   
  26.     /* * * */  
  27.     /* 
  28.      * Display functions 
  29.      */  
  30.   
  31.   
  32.     /* 
  33.      * Get the bounds of a display 
  34.      */  
  35.     int (*GetDisplayBounds) (_THIS, SDL_VideoDisplay * display, SDL_Rect * rect);  
  36.   
  37.   
  38.     /* 
  39.      * Get a list of the available display modes for a display. 
  40.      */  
  41.     void (*GetDisplayModes) (_THIS, SDL_VideoDisplay * display);  
  42.   
  43.   
  44.     /* 
  45.      * Setting the display mode is independent of creating windows, so 
  46.      * when the display mode is changed, all existing windows should have 
  47.      * their data updated accordingly, including the display surfaces 
  48.      * associated with them. 
  49.      */  
  50.     int (*SetDisplayMode) (_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode);  
  51.   
  52.   
  53.     /* * * */  
  54.     /* 
  55.      * Window functions 
  56.      */  
  57.     int (*CreateWindow) (_THIS, SDL_Window * window);  
  58.     int (*CreateWindowFrom) (_THIS, SDL_Window * window, const void *data);  
  59.     void (*SetWindowTitle) (_THIS, SDL_Window * window);  
  60.     void (*SetWindowIcon) (_THIS, SDL_Window * window, SDL_Surface * icon);  
  61.     void (*SetWindowPosition) (_THIS, SDL_Window * window);  
  62.     void (*SetWindowSize) (_THIS, SDL_Window * window);  
  63.     void (*SetWindowMinimumSize) (_THIS, SDL_Window * window);  
  64.     void (*SetWindowMaximumSize) (_THIS, SDL_Window * window);  
  65.     void (*ShowWindow) (_THIS, SDL_Window * window);  
  66.     void (*HideWindow) (_THIS, SDL_Window * window);  
  67.     void (*RaiseWindow) (_THIS, SDL_Window * window);  
  68.     void (*MaximizeWindow) (_THIS, SDL_Window * window);  
  69.     void (*MinimizeWindow) (_THIS, SDL_Window * window);  
  70.     void (*RestoreWindow) (_THIS, SDL_Window * window);  
  71.     void (*SetWindowBordered) (_THIS, SDL_Window * window, SDL_bool bordered);  
  72.     void (*SetWindowFullscreen) (_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen);  
  73.     int (*SetWindowGammaRamp) (_THIS, SDL_Window * window, const Uint16 * ramp);  
  74.     int (*GetWindowGammaRamp) (_THIS, SDL_Window * window, Uint16 * ramp);  
  75.     void (*SetWindowGrab) (_THIS, SDL_Window * window, SDL_bool grabbed);  
  76.     void (*DestroyWindow) (_THIS, SDL_Window * window);  
  77.     int (*CreateWindowFramebuffer) (_THIS, SDL_Window * window, Uint32 * format, void ** pixels, int *pitch);  
  78.     int (*UpdateWindowFramebuffer) (_THIS, SDL_Window * window, const SDL_Rect * rects, int numrects);  
  79.     void (*DestroyWindowFramebuffer) (_THIS, SDL_Window * window);  
  80.     void (*OnWindowEnter) (_THIS, SDL_Window * window);  
  81.   
  82.   
  83.     /* * * */  
  84.     /* 
  85.      * Shaped-window functions 
  86.      */  
  87.     SDL_ShapeDriver shape_driver;  
  88.   
  89.   
  90.     /* Get some platform dependent window information */  
  91.       SDL_bool(*GetWindowWMInfo) (_THIS, SDL_Window * window,  
  92.                                   struct SDL_SysWMinfo * info);  
  93.   
  94.   
  95.     /* * * */  
  96.     /* 
  97.      * OpenGL support 
  98.      */  
  99.     int (*GL_LoadLibrary) (_THIS, const char *path);  
  100.     void *(*GL_GetProcAddress) (_THIS, const char *proc);  
  101.     void (*GL_UnloadLibrary) (_THIS);  
  102.       SDL_GLContext(*GL_CreateContext) (_THIS, SDL_Window * window);  
  103.     int (*GL_MakeCurrent) (_THIS, SDL_Window * window, SDL_GLContext context);  
  104.     void (*GL_GetDrawableSize) (_THIS, SDL_Window * window, int *w, int *h);  
  105.     int (*GL_SetSwapInterval) (_THIS, int interval);  
  106.     int (*GL_GetSwapInterval) (_THIS);  
  107.     void (*GL_SwapWindow) (_THIS, SDL_Window * window);  
  108.     void (*GL_DeleteContext) (_THIS, SDL_GLContext context);  
  109.   
  110.   
  111.     /* * * */  
  112.     /* 
  113.      * Event manager functions 
  114.      */  
  115.     void (*PumpEvents) (_THIS);  
  116.   
  117.   
  118.     /* Suspend the screensaver */  
  119.     void (*SuspendScreenSaver) (_THIS);  
  120.   
  121.   
  122.     /* Text input */  
  123.     void (*StartTextInput) (_THIS);  
  124.     void (*StopTextInput) (_THIS);  
  125.     void (*SetTextInputRect) (_THIS, SDL_Rect *rect);  
  126.   
  127.   
  128.     /* Screen keyboard */  
  129.     SDL_bool (*HasScreenKeyboardSupport) (_THIS);  
  130.     void (*ShowScreenKeyboard) (_THIS, SDL_Window *window);  
  131.     void (*HideScreenKeyboard) (_THIS, SDL_Window *window);  
  132.     SDL_bool (*IsScreenKeyboardShown) (_THIS, SDL_Window *window);  
  133.   
  134.   
  135.     /* Clipboard */  
  136.     int (*SetClipboardText) (_THIS, const char *text);  
  137.     char * (*GetClipboardText) (_THIS);  
  138.     SDL_bool (*HasClipboardText) (_THIS);  
  139.   
  140.   
  141.     /* MessageBox */  
  142.     int (*ShowMessageBox) (_THIS, const SDL_MessageBoxData *messageboxdata, int *buttonid);  
  143.   
  144.   
  145.     /* * * */  
  146.     /* Data common to all drivers */  
  147.     SDL_bool suspend_screensaver;  
  148.     int num_displays;  
  149.     SDL_VideoDisplay *displays;  
  150.     SDL_Window *windows;  
  151.     Uint8 window_magic;  
  152.     Uint32 next_object_id;  
  153.     char * clipboard_text;  
  154.   
  155.   
  156.     /* * * */  
  157.     /* Data used by the GL drivers */  
  158.     struct  
  159.     {  
  160.         int red_size;  
  161.         int green_size;  
  162.         int blue_size;  
  163.         int alpha_size;  
  164.         int depth_size;  
  165.         int buffer_size;  
  166.         int stencil_size;  
  167.         int double_buffer;  
  168.         int accum_red_size;  
  169.         int accum_green_size;  
  170.         int accum_blue_size;  
  171.         int accum_alpha_size;  
  172.         int stereo;  
  173.         int multisamplebuffers;  
  174.         int multisamplesamples;  
  175.         int accelerated;  
  176.         int major_version;  
  177.         int minor_version;  
  178.         int flags;  
  179.         int profile_mask;  
  180.         int share_with_current_context;  
  181.         int framebuffer_srgb_capable;  
  182.         int retained_backing;  
  183.         int driver_loaded;  
  184.         char driver_path[256];  
  185.         void *dll_handle;  
  186.     } gl_config;  
  187.   
  188.   
  189.     /* * * */  
  190.     /* Cache current GL context; don't call the OS when it hasn't changed. */  
  191.     /* We have the global pointers here so Cocoa continues to work the way 
  192.        it always has, and the thread-local storage for the general case. 
  193.      */  
  194.     SDL_Window *current_glwin;  
  195.     SDL_GLContext current_glctx;  
  196.     SDL_TLSID current_glwin_tls;  
  197.     SDL_TLSID current_glctx_tls;  
  198.   
  199.   
  200.     /* * * */  
  201.     /* Data private to this driver */  
  202.     void *driverdata;  
  203.     struct SDL_GLDriverData *gl_data;  
  204.       
  205. #if SDL_VIDEO_OPENGL_EGL  
  206.     struct SDL_EGL_VideoData *egl_data;  
  207. #endif  
  208.       
  209. #if SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2  
  210.     struct SDL_PrivateGLESData *gles_data;  
  211. #endif  
  212.   
  213.   
  214.     /* * * */  
  215.     /* The function used to dispose of this structure */  
  216.     void (*free) (_THIS);  
  217. };  
struct SDL_VideoDevice
{
    /* * * */
    /* The name of this video driver */
    const char *name;


    /* * * */
    /* Initialization/Query functions */


    /*
     * Initialize the native video subsystem, filling in the list of
     * displays for this driver, returning 0 or -1 if there's an error.
     */
    int (*VideoInit) (_THIS);


    /*
     * Reverse the effects VideoInit() -- called if VideoInit() fails or
     * if the application is shutting down the video subsystem.
     */
    void (*VideoQuit) (_THIS);


    /* * * */
    /*
     * Display functions
     */


    /*
     * Get the bounds of a display
     */
    int (*GetDisplayBounds) (_THIS, SDL_VideoDisplay * display, SDL_Rect * rect);


    /*
     * Get a list of the available display modes for a display.
     */
    void (*GetDisplayModes) (_THIS, SDL_VideoDisplay * display);


    /*
     * Setting the display mode is independent of creating windows, so
     * when the display mode is changed, all existing windows should have
     * their data updated accordingly, including the display surfaces
     * associated with them.
     */
    int (*SetDisplayMode) (_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode);


    /* * * */
    /*
     * Window functions
     */
    int (*CreateWindow) (_THIS, SDL_Window * window);
    int (*CreateWindowFrom) (_THIS, SDL_Window * window, const void *data);
    void (*SetWindowTitle) (_THIS, SDL_Window * window);
    void (*SetWindowIcon) (_THIS, SDL_Window * window, SDL_Surface * icon);
    void (*SetWindowPosition) (_THIS, SDL_Window * window);
    void (*SetWindowSize) (_THIS, SDL_Window * window);
    void (*SetWindowMinimumSize) (_THIS, SDL_Window * window);
    void (*SetWindowMaximumSize) (_THIS, SDL_Window * window);
    void (*ShowWindow) (_THIS, SDL_Window * window);
    void (*HideWindow) (_THIS, SDL_Window * window);
    void (*RaiseWindow) (_THIS, SDL_Window * window);
    void (*MaximizeWindow) (_THIS, SDL_Window * window);
    void (*MinimizeWindow) (_THIS, SDL_Window * window);
    void (*RestoreWindow) (_THIS, SDL_Window * window);
    void (*SetWindowBordered) (_THIS, SDL_Window * window, SDL_bool bordered);
    void (*SetWindowFullscreen) (_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen);
    int (*SetWindowGammaRamp) (_THIS, SDL_Window * window, const Uint16 * ramp);
    int (*GetWindowGammaRamp) (_THIS, SDL_Window * window, Uint16 * ramp);
    void (*SetWindowGrab) (_THIS, SDL_Window * window, SDL_bool grabbed);
    void (*DestroyWindow) (_THIS, SDL_Window * window);
    int (*CreateWindowFramebuffer) (_THIS, SDL_Window * window, Uint32 * format, void ** pixels, int *pitch);
    int (*UpdateWindowFramebuffer) (_THIS, SDL_Window * window, const SDL_Rect * rects, int numrects);
    void (*DestroyWindowFramebuffer) (_THIS, SDL_Window * window);
    void (*OnWindowEnter) (_THIS, SDL_Window * window);


    /* * * */
    /*
     * Shaped-window functions
     */
    SDL_ShapeDriver shape_driver;


    /* Get some platform dependent window information */
      SDL_bool(*GetWindowWMInfo) (_THIS, SDL_Window * window,
                                  struct SDL_SysWMinfo * info);


    /* * * */
    /*
     * OpenGL support
     */
    int (*GL_LoadLibrary) (_THIS, const char *path);
    void *(*GL_GetProcAddress) (_THIS, const char *proc);
    void (*GL_UnloadLibrary) (_THIS);
      SDL_GLContext(*GL_CreateContext) (_THIS, SDL_Window * window);
    int (*GL_MakeCurrent) (_THIS, SDL_Window * window, SDL_GLContext context);
    void (*GL_GetDrawableSize) (_THIS, SDL_Window * window, int *w, int *h);
    int (*GL_SetSwapInterval) (_THIS, int interval);
    int (*GL_GetSwapInterval) (_THIS);
    void (*GL_SwapWindow) (_THIS, SDL_Window * window);
    void (*GL_DeleteContext) (_THIS, SDL_GLContext context);


    /* * * */
    /*
     * Event manager functions
     */
    void (*PumpEvents) (_THIS);


    /* Suspend the screensaver */
    void (*SuspendScreenSaver) (_THIS);


    /* Text input */
    void (*StartTextInput) (_THIS);
    void (*StopTextInput) (_THIS);
    void (*SetTextInputRect) (_THIS, SDL_Rect *rect);


    /* Screen keyboard */
    SDL_bool (*HasScreenKeyboardSupport) (_THIS);
    void (*ShowScreenKeyboard) (_THIS, SDL_Window *window);
    void (*HideScreenKeyboard) (_THIS, SDL_Window *window);
    SDL_bool (*IsScreenKeyboardShown) (_THIS, SDL_Window *window);


    /* Clipboard */
    int (*SetClipboardText) (_THIS, const char *text);
    char * (*GetClipboardText) (_THIS);
    SDL_bool (*HasClipboardText) (_THIS);


    /* MessageBox */
    int (*ShowMessageBox) (_THIS, const SDL_MessageBoxData *messageboxdata, int *buttonid);


    /* * * */
    /* Data common to all drivers */
    SDL_bool suspend_screensaver;
    int num_displays;
    SDL_VideoDisplay *displays;
    SDL_Window *windows;
    Uint8 window_magic;
    Uint32 next_object_id;
    char * clipboard_text;


    /* * * */
    /* Data used by the GL drivers */
    struct
    {
        int red_size;
        int green_size;
        int blue_size;
        int alpha_size;
        int depth_size;
        int buffer_size;
        int stencil_size;
        int double_buffer;
        int accum_red_size;
        int accum_green_size;
        int accum_blue_size;
        int accum_alpha_size;
        int stereo;
        int multisamplebuffers;
        int multisamplesamples;
        int accelerated;
        int major_version;
        int minor_version;
        int flags;
        int profile_mask;
        int share_with_current_context;
        int framebuffer_srgb_capable;
        int retained_backing;
        int driver_loaded;
        char driver_path[256];
        void *dll_handle;
    } gl_config;


    /* * * */
    /* Cache current GL context; don't call the OS when it hasn't changed. */
    /* We have the global pointers here so Cocoa continues to work the way
       it always has, and the thread-local storage for the general case.
     */
    SDL_Window *current_glwin;
    SDL_GLContext current_glctx;
    SDL_TLSID current_glwin_tls;
    SDL_TLSID current_glctx_tls;


    /* * * */
    /* Data private to this driver */
    void *driverdata;
    struct SDL_GLDriverData *gl_data;
    
#if SDL_VIDEO_OPENGL_EGL
    struct SDL_EGL_VideoData *egl_data;
#endif
    
#if SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
    struct SDL_PrivateGLESData *gles_data;
#endif


    /* * * */
    /* The function used to dispose of this structure */
    void (*free) (_THIS);
};

这个结构体包含了一大堆的函数指针。这些指针在前文所说的VideoBootStrap的create()方法调用的时候会被赋值。SDL通过调用这些函数指针,完成视频显示的各项内容。


3. 调用选中的SDL_VideoDevice的VideoInit()方法。
选择了合适的SDL_VideoDevice之后,调用该SDL_VideoDevice的VideoInit()就可以真正的初始化视频驱动了。以Windows系统为例。从前文的函数中可以看出,Windows系统的VideoInit()接口实际上调用了WIN_VideoInit()函数。我们来看一下WIN_VideoInit()函数的定义(位于video\windows\SDL_windowsvideo.c)。
[cpp] view plain copy
print ?
  1. int WIN_VideoInit(_THIS)  
  2. {  
  3.     if (WIN_InitModes(_this) < 0) {  
  4.         return -1;  
  5.     }  
  6.   
  7.   
  8.     WIN_InitKeyboard(_this);  
  9.     WIN_InitMouse(_this);  
  10.   
  11.   
  12.     return 0;  
  13. }  
int WIN_VideoInit(_THIS)
{
    if (WIN_InitModes(_this) < 0) {
        return -1;
    }


    WIN_InitKeyboard(_this);
    WIN_InitMouse(_this);


    return 0;
}


其中有3个函数:WIN_InitModes(),WIN_InitKeyboard(),WIN_InitMouse()。后两个函数用于初始化键盘和鼠标,我们暂且不研究。看一下WIN_InitModes()的函数。
[cpp] view plain copy
print ?
  1. int WIN_InitModes(_THIS)  
  2. {  
  3.     int pass;  
  4.     DWORD i, j, count;  
  5.     DISPLAY_DEVICE device;  
  6.   
  7.   
  8.     device.cb = sizeof(device);  
  9.   
  10.   
  11.     /* Get the primary display in the first pass */  
  12.     for (pass = 0; pass < 2; ++pass) {  
  13.         for (i = 0; ; ++i) {  
  14.             TCHAR DeviceName[32];  
  15.   
  16.   
  17.             if (!EnumDisplayDevices(NULL, i, &device, 0)) {  
  18.                 break;  
  19.             }  
  20.             if (!(device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) {  
  21.                 continue;  
  22.             }  
  23.             if (pass == 0) {  
  24.                 if (!(device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)) {  
  25.                     continue;  
  26.                 }  
  27.             } else {  
  28.                 if (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {  
  29.                     continue;  
  30.                 }  
  31.             }  
  32.             SDL_memcpy(DeviceName, device.DeviceName, sizeof(DeviceName));  
  33. #ifdef DEBUG_MODES  
  34.             printf("Device: %s\n", WIN_StringToUTF8(DeviceName));  
  35. #endif  
  36.             count = 0;  
  37.             for (j = 0; ; ++j) {  
  38.                 if (!EnumDisplayDevices(DeviceName, j, &device, 0)) {  
  39.                     break;  
  40.                 }  
  41.                 if (!(device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) {  
  42.                     continue;  
  43.                 }  
  44.                 if (pass == 0) {  
  45.                     if (!(device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)) {  
  46.                         continue;  
  47.                     }  
  48.                 } else {  
  49.                     if (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {  
  50.                         continue;  
  51.                     }  
  52.                 }  
  53.                 count += WIN_AddDisplay(device.DeviceName);  
  54.             }  
  55.             if (count == 0) {  
  56.                 WIN_AddDisplay(DeviceName);  
  57.             }  
  58.         }  
  59.     }  
  60.     if (_this->num_displays == 0) {  
  61.         return SDL_SetError("No displays available");  
  62.     }  
  63.     return 0;  
  64. }  
int WIN_InitModes(_THIS)
{
    int pass;
    DWORD i, j, count;
    DISPLAY_DEVICE device;


    device.cb = sizeof(device);


    /* Get the primary display in the first pass */
    for (pass = 0; pass < 2; ++pass) {
        for (i = 0; ; ++i) {
            TCHAR DeviceName[32];


            if (!EnumDisplayDevices(NULL, i, &device, 0)) {
                break;
            }
            if (!(device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) {
                continue;
            }
            if (pass == 0) {
                if (!(device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)) {
                    continue;
                }
            } else {
                if (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
                    continue;
                }
            }
            SDL_memcpy(DeviceName, device.DeviceName, sizeof(DeviceName));
#ifdef DEBUG_MODES
            printf("Device: %s\n", WIN_StringToUTF8(DeviceName));
#endif
            count = 0;
            for (j = 0; ; ++j) {
                if (!EnumDisplayDevices(DeviceName, j, &device, 0)) {
                    break;
                }
                if (!(device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) {
                    continue;
                }
                if (pass == 0) {
                    if (!(device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)) {
                        continue;
                    }
                } else {
                    if (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
                        continue;
                    }
                }
                count += WIN_AddDisplay(device.DeviceName);
            }
            if (count == 0) {
                WIN_AddDisplay(DeviceName);
            }
        }
    }
    if (_this->num_displays == 0) {
        return SDL_SetError("No displays available");
    }
    return 0;
}


该函数的作用就是获得系统中显示设备的信息。目前还没有深入研究,待有时间再补上该函数的分析。

2.AUDIO(音频)

音频子系统的初始化函数是SDL_ AudioInit ()。它的源代码位于audio\SDL_audio.c文件中,如下所示。

[cpp] view plain copy
print ?
  1. int SDL_AudioInit(const char *driver_name)  
  2. {  
  3.     int i = 0;  
  4.     int initialized = 0;  
  5.     int tried_to_init = 0;  
  6.   
  7.   
  8.     if (SDL_WasInit(SDL_INIT_AUDIO)) {  
  9.         SDL_AudioQuit();        /* shutdown driver if already running. */  
  10.     }  
  11.   
  12.   
  13.     SDL_memset(¤t_audio, '\0'sizeof(current_audio));  
  14.     SDL_memset(open_devices, '\0'sizeof(open_devices));  
  15.   
  16.   
  17.     /* Select the proper audio driver */  
  18.     if (driver_name == NULL) {  
  19.         driver_name = SDL_getenv("SDL_AUDIODRIVER");  
  20.     }  
  21.   
  22.   
  23.     for (i = 0; (!initialized) && (bootstrap[i]); ++i) {  
  24.         /* make sure we should even try this driver before doing so... */  
  25.         const AudioBootStrap *backend = bootstrap[i];  
  26.         if ((driver_name && (SDL_strncasecmp(backend->name, driver_name, SDL_strlen(driver_name)) != 0)) ||  
  27.             (!driver_name && backend->demand_only)) {  
  28.             continue;  
  29.         }  
  30.   
  31.   
  32.         tried_to_init = 1;  
  33.         SDL_memset(¤t_audio, 0, sizeof(current_audio));  
  34.         current_audio.name = backend->name;  
  35.         current_audio.desc = backend->desc;  
  36.         initialized = backend->init(¤t_audio.impl);  
  37.     }  
  38.   
  39.   
  40.     if (!initialized) {  
  41.         /* specific drivers will set the error message if they fail... */  
  42.         if (!tried_to_init) {  
  43.             if (driver_name) {  
  44.                 SDL_SetError("Audio target '%s' not available", driver_name);  
  45.             } else {  
  46.                 SDL_SetError("No available audio device");  
  47.             }  
  48.         }  
  49.   
  50.   
  51.         SDL_memset(¤t_audio, 0, sizeof(current_audio));  
  52.         return (-1);            /* No driver was available, so fail. */  
  53.     }  
  54.   
  55.   
  56.     finalize_audio_entry_points();  
  57.   
  58.   
  59.     return (0);  
  60. }  
int SDL_AudioInit(const char *driver_name)
{
    int i = 0;
    int initialized = 0;
    int tried_to_init = 0;


    if (SDL_WasInit(SDL_INIT_AUDIO)) {
        SDL_AudioQuit();        /* shutdown driver if already running. */
    }


    SDL_memset(¤t_audio, '\0', sizeof(current_audio));
    SDL_memset(open_devices, '\0', sizeof(open_devices));


    /* Select the proper audio driver */
    if (driver_name == NULL) {
        driver_name = SDL_getenv("SDL_AUDIODRIVER");
    }


    for (i = 0; (!initialized) && (bootstrap[i]); ++i) {
        /* make sure we should even try this driver before doing so... */
        const AudioBootStrap *backend = bootstrap[i];
        if ((driver_name && (SDL_strncasecmp(backend->name, driver_name, SDL_strlen(driver_name)) != 0)) ||
            (!driver_name && backend->demand_only)) {
            continue;
        }


        tried_to_init = 1;
        SDL_memset(¤t_audio, 0, sizeof(current_audio));
        current_audio.name = backend->name;
        current_audio.desc = backend->desc;
        initialized = backend->init(¤t_audio.impl);
    }


    if (!initialized) {
        /* specific drivers will set the error message if they fail... */
        if (!tried_to_init) {
            if (driver_name) {
                SDL_SetError("Audio target '%s' not available", driver_name);
            } else {
                SDL_SetError("No available audio device");
            }
        }


        SDL_memset(¤t_audio, 0, sizeof(current_audio));
        return (-1);            /* No driver was available, so fail. */
    }


    finalize_audio_entry_points();


    return (0);
}


音频初始化和视频很类似,比视频简单一些,关键在于选择一个合适的SDL_AudioDriver。


在这里,涉及到两个重要的结构体:SDL_AudioDriver以及AudioBootStrap。其中SDL_AudioDriver代表了一个音频驱动程序。AudioBootStrap从字面上理解是“音频驱动程序的引导程序”,即用于创建一个SDL_AudioDriver。可以看出音频子系统中的结构体和视频子系统中的结构体的格式基本上是一模一样的。我们先来看看AudioBootStrap这个结构体。它的定义如下(位于audio\SDL_sysaudio.h)。
[cpp] view plain copy
print ?
  1. typedef struct AudioBootStrap  
  2. {  
  3.     const char *name;  
  4.     const char *desc;  
  5.     int (*init) (SDL_AudioDriverImpl * impl);  
  6.     int demand_only;  /* 1==request explicitly, or it won't be available. */  
  7. } AudioBootStrap;  
typedef struct AudioBootStrap
{
    const char *name;
    const char *desc;
    int (*init) (SDL_AudioDriverImpl * impl);
    int demand_only;  /* 1==request explicitly, or it won't be available. */
} AudioBootStrap;

可以看出它的定义比较简单,每个字段的含义如下:
name:驱动名称
desc:描述
init():创建SDL_AudioDriver的函数指针
demand_only:没有研究过。

SDL中有一个AudioBootStrap类型的静态数组bootstrap。用于存储SDL支持的音频驱动程序。该静态数组定义如下(位于audio\SDL_audio.c)。
[cpp] view plain copy
print ?
  1. /* Available audio drivers */  
  2. static const AudioBootStrap *const bootstrap[] = {  
  3. #if SDL_AUDIO_DRIVER_PULSEAUDIO  
  4.     &PULSEAUDIO_bootstrap,  
  5. #endif  
  6. #if SDL_AUDIO_DRIVER_ALSA  
  7.     &ALSA_bootstrap,  
  8. #endif  
  9. #if SDL_AUDIO_DRIVER_SNDIO  
  10.     &SNDIO_bootstrap,  
  11. #endif  
  12. #if SDL_AUDIO_DRIVER_BSD  
  13.     &BSD_AUDIO_bootstrap,  
  14. #endif  
  15. #if SDL_AUDIO_DRIVER_OSS  
  16.     &DSP_bootstrap,  
  17. #endif  
  18. #if SDL_AUDIO_DRIVER_QSA  
  19.     &QSAAUDIO_bootstrap,  
  20. #endif  
  21. #if SDL_AUDIO_DRIVER_SUNAUDIO  
  22.     &SUNAUDIO_bootstrap,  
  23. #endif  
  24. #if SDL_AUDIO_DRIVER_ARTS  
  25.     &ARTS_bootstrap,  
  26. #endif  
  27. #if SDL_AUDIO_DRIVER_ESD  
  28.     &ESD_bootstrap,  
  29. #endif  
  30. #if SDL_AUDIO_DRIVER_NAS  
  31.     &NAS_bootstrap,  
  32. #endif  
  33. #if SDL_AUDIO_DRIVER_XAUDIO2  
  34.     &XAUDIO2_bootstrap,  
  35. #endif  
  36. #if SDL_AUDIO_DRIVER_DSOUND  
  37.     &DSOUND_bootstrap,  
  38. #endif  
  39. #if SDL_AUDIO_DRIVER_WINMM  
  40.     &WINMM_bootstrap,  
  41. #endif  
  42. #if SDL_AUDIO_DRIVER_PAUDIO  
  43.     &PAUDIO_bootstrap,  
  44. #endif  
  45. #if SDL_AUDIO_DRIVER_HAIKU  
  46.     &HAIKUAUDIO_bootstrap,  
  47. #endif  
  48. #if SDL_AUDIO_DRIVER_COREAUDIO  
  49.     &COREAUDIO_bootstrap,  
  50. #endif  
  51. #if SDL_AUDIO_DRIVER_DISK  
  52.     &DISKAUD_bootstrap,  
  53. #endif  
  54. #if SDL_AUDIO_DRIVER_DUMMY  
  55.     &DUMMYAUD_bootstrap,  
  56. #endif  
  57. #if SDL_AUDIO_DRIVER_FUSIONSOUND  
  58.     &FUSIONSOUND_bootstrap,  
  59. #endif  
  60. #if SDL_AUDIO_DRIVER_ANDROID  
  61.     &ANDROIDAUD_bootstrap,  
  62. #endif  
  63. #if SDL_AUDIO_DRIVER_PSP  
  64.     &PSPAUD_bootstrap,  
  65. #endif  
  66.     NULL  
  67. };  
/* Available audio drivers */
static const AudioBootStrap *const bootstrap[] = {
#if SDL_AUDIO_DRIVER_PULSEAUDIO
    &PULSEAUDIO_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_ALSA
    &ALSA_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_SNDIO
    &SNDIO_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_BSD
    &BSD_AUDIO_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_OSS
    &DSP_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_QSA
    &QSAAUDIO_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_SUNAUDIO
    &SUNAUDIO_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_ARTS
    &ARTS_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_ESD
    &ESD_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_NAS
    &NAS_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_XAUDIO2
    &XAUDIO2_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_DSOUND
    &DSOUND_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_WINMM
    &WINMM_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_PAUDIO
    &PAUDIO_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_HAIKU
    &HAIKUAUDIO_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_COREAUDIO
    &COREAUDIO_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_DISK
    &DISKAUD_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_DUMMY
    &DUMMYAUD_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_FUSIONSOUND
    &FUSIONSOUND_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_ANDROID
    &ANDROIDAUD_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_PSP
    &PSPAUD_bootstrap,
#endif
    NULL
};


在这里我们可以看一下DirectSound的AudioBootStrap的变量DSOUND_bootstrap(audio\directsound\SDL_directsound.c)。
[cpp] view plain copy
print ?
  1. AudioBootStrap DSOUND_bootstrap = {  
  2.     "directsound""DirectSound", DSOUND_Init, 0  
  3. };  
AudioBootStrap DSOUND_bootstrap = {
    "directsound", "DirectSound", DSOUND_Init, 0
};

可以看出该音频驱动名称为“directsound”,描述为“DirectSound”,创建SDL_AudioDriver的函数为“DSOUND_Init()”。
下面看一下DirectSound初始化函数DSOUND_Init()的定义。
[cpp] view plain copy
print ?
  1. static int DSOUND_Init(SDL_AudioDriverImpl * impl)  
  2. {  
  3.     if (!DSOUND_Load()) {  
  4.         return 0;  
  5.     }  
  6.   
  7.   
  8.     /* Set the function pointers */  
  9.     impl->DetectDevices = DSOUND_DetectDevices;  
  10.     impl->OpenDevice = DSOUND_OpenDevice;  
  11.     impl->PlayDevice = DSOUND_PlayDevice;  
  12.     impl->WaitDevice = DSOUND_WaitDevice;  
  13.     impl->WaitDone = DSOUND_WaitDone;  
  14.     impl->GetDeviceBuf = DSOUND_GetDeviceBuf;  
  15.     impl->CloseDevice = DSOUND_CloseDevice;  
  16.     impl->Deinitialize = DSOUND_Deinitialize;  
  17.   
  18.   
  19.     return 1;   /* this audio target is available. */  
  20. }  
static int DSOUND_Init(SDL_AudioDriverImpl * impl)
{
    if (!DSOUND_Load()) {
        return 0;
    }


    /* Set the function pointers */
    impl->DetectDevices = DSOUND_DetectDevices;
    impl->OpenDevice = DSOUND_OpenDevice;
    impl->PlayDevice = DSOUND_PlayDevice;
    impl->WaitDevice = DSOUND_WaitDevice;
    impl->WaitDone = DSOUND_WaitDone;
    impl->GetDeviceBuf = DSOUND_GetDeviceBuf;
    impl->CloseDevice = DSOUND_CloseDevice;
    impl->Deinitialize = DSOUND_Deinitialize;


    return 1;   /* this audio target is available. */
}


和视频驱动的初始化一样,音频驱动初始化也是对SDL_AudioDriver的接口指针进行赋值。在这里涉及到了一个DirectSound的加载函数DSOUND_Load(),我们可以看一下它的代码。
[cpp] view plain copy
print ?
  1. static int DSOUND_Load(void)  
  2. {  
  3.     int loaded = 0;  
  4.   
  5.   
  6.     DSOUND_Unload();  
  7.   
  8.   
  9.     DSoundDLL = SDL_LoadObject("DSOUND.DLL");  
  10.     if (DSoundDLL == NULL) {  
  11.         SDL_SetError("DirectSound: failed to load DSOUND.DLL");  
  12.     } else {  
  13.         /* Now make sure we have DirectX 8 or better... */  
  14.         #define DSOUNDLOAD(f) { \  
  15.             p##f = (fn##f) SDL_LoadFunction(DSoundDLL, #f); \  
  16.             if (!p##f) loaded = 0; \  
  17.         }  
  18.         loaded = 1;  /* will reset if necessary. */  
  19.         DSOUNDLOAD(DirectSoundCreate8);  
  20.         DSOUNDLOAD(DirectSoundEnumerateW);  
  21.         DSOUNDLOAD(DirectSoundCaptureEnumerateW);  
  22.         #undef DSOUNDLOAD  
  23.   
  24.   
  25.         if (!loaded) {  
  26.             SDL_SetError("DirectSound: System doesn't appear to have DX8.");  
  27.         }  
  28.     }  
  29.   
  30.   
  31.     if (!loaded) {  
  32.         DSOUND_Unload();  
  33.     }  
  34.   
  35.   
  36.     return loaded;  
  37. }  
static int DSOUND_Load(void)
{
    int loaded = 0;


    DSOUND_Unload();


    DSoundDLL = SDL_LoadObject("DSOUND.DLL");
    if (DSoundDLL == NULL) {
        SDL_SetError("DirectSound: failed to load DSOUND.DLL");
    } else {
        /* Now make sure we have DirectX 8 or better... */
        #define DSOUNDLOAD(f) { \
            p##f = (fn##f) SDL_LoadFunction(DSoundDLL, #f); \
            if (!p##f) loaded = 0; \
        }
        loaded = 1;  /* will reset if necessary. */
        DSOUNDLOAD(DirectSoundCreate8);
        DSOUNDLOAD(DirectSoundEnumerateW);
        DSOUNDLOAD(DirectSoundCaptureEnumerateW);
        #undef DSOUNDLOAD


        if (!loaded) {
            SDL_SetError("DirectSound: System doesn't appear to have DX8.");
        }
    }


    if (!loaded) {
        DSOUND_Unload();
    }


    return loaded;
}


从代码中可以看出,该函数加载了“DSOUND.DLL”的DirectSoundCreate8(),DirectSoundEnumerateW(),DirectSoundCaptureEnumerateW()函数。

你可能感兴趣的:(SDL2)