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

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的初始化函数SDL_Init()。这篇文章继续分析SDL的源代码。本文分析SDL的窗口(SDL_Window)。


SDL播放视频的代码流程如下所示。

初始化: 

SDL_Init(): 初始化SDL。

SDL_CreateWindow(): 创建窗口(Window)。

SDL_CreateRenderer(): 基于窗口创建渲染器(Render)。

SDL_CreateTexture(): 创建纹理(Texture)。

循环渲染数据: 

SDL_UpdateTexture(): 设置纹理的数据。

SDL_RenderCopy(): 纹理复制给渲染器。 

SDL_RenderPresent(): 显示。

上篇文章分析了该流程中的第一个函数SDL_Init()。本文继续分析该流程中的第二个函数SDL_CreateWindow()

SDL_Window

SDL_Window结构体定义了一个SDL2中的窗口。如果直接使用SDL2编译好的SDK的话,是看不到它的内部结构的。有关它的定义在头文件中只有一行代码,但是这一行定义前面的注释非常之多,如下所示:

/**
 *  \brief The type used to identify a window
 *
 *  \sa SDL_CreateWindow()
 *  \sa SDL_CreateWindowFrom()
 *  \sa SDL_DestroyWindow()
 *  \sa SDL_GetWindowData()
 *  \sa SDL_GetWindowFlags()
 *  \sa SDL_GetWindowGrab()
 *  \sa SDL_GetWindowPosition()
 *  \sa SDL_GetWindowSize()
 *  \sa SDL_GetWindowTitle()
 *  \sa SDL_HideWindow()
 *  \sa SDL_MaximizeWindow()
 *  \sa SDL_MinimizeWindow()
 *  \sa SDL_RaiseWindow()
 *  \sa SDL_RestoreWindow()
 *  \sa SDL_SetWindowData()
 *  \sa SDL_SetWindowFullscreen()
 *  \sa SDL_SetWindowGrab()
 *  \sa SDL_SetWindowIcon()
 *  \sa SDL_SetWindowPosition()
 *  \sa SDL_SetWindowSize()
 *  \sa SDL_SetWindowBordered()
 *  \sa SDL_SetWindowTitle()
 *  \sa SDL_ShowWindow()
 */
typedef struct SDL_Window SDL_Window;

在源代码工程中可以看到它的定义,位于video\SDL_sysvideo.h文件中。它的定义如下。

/* Define the SDL window structure, corresponding to toplevel windows */
struct SDL_Window
{
    const void *magic;
    Uint32 id;
    char *title;
    SDL_Surface *icon;
    int x, y;
    int w, h;
    int min_w, min_h;
    int max_w, max_h;
    Uint32 flags;
    Uint32 last_fullscreen_flags;

    /* Stored position and size for windowed mode */
    SDL_Rect windowed;

    SDL_DisplayMode fullscreen_mode;

    float brightness;
    Uint16 *gamma;
    Uint16 *saved_gamma;        /* (just offset into gamma) */

    SDL_Surface *surface;
    SDL_bool surface_valid;

    SDL_bool is_destroying;

    SDL_WindowShaper *shaper;

    SDL_WindowUserData *data;

    void *driverdata;

    SDL_Window *prev;
    SDL_Window *next;
};

可以看出其中包含了一个“窗口”应该包含的各种属性。这个结构体中的各个变量还没有深入研究,暂不详细分析。下面来看看如何创建这个SDL_Window。

SDL_CreateWindow()


函数简介

SDL_CreateWindow()用于创建一个视频播放的窗口。SDL_CreateWindow()的原型如下。

SDL_Window * SDLCALL SDL_CreateWindow(const char *title,
                                                      int x, int y, int w,
                                                      int h, Uint32 flags);

参数含义如下。
title :窗口标题
x :窗口位置x坐标。也可以设置为SDL_WINDOWPOS_CENTERED或SDL_WINDOWPOS_UNDEFINED。
y :窗口位置y坐标。同上。
w :窗口的宽
h :窗口的高
flags :支持下列标识。包括了窗口的是否最大化、最小化,能否调整边界等等属性。
       ::SDL_WINDOW_FULLSCREEN,    ::SDL_WINDOW_OPENGL,
       ::SDL_WINDOW_HIDDEN,        ::SDL_WINDOW_BORDERLESS,
       ::SDL_WINDOW_RESIZABLE,     ::SDL_WINDOW_MAXIMIZED,
       ::SDL_WINDOW_MINIMIZED,     ::SDL_WINDOW_INPUT_GRABBED,
       ::SDL_WINDOW_ALLOW_HIGHDPI.

返回创建完成的窗口的ID。如果创建失败则返回0。


函数调用关系图

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


上面的函数调用关系图本来是一张高清大图,但是由于博客对图片尺寸有限制,因而显得不太清晰。相册里面上传了一份原始的大图片:

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

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

源代码分析

SDL_CreateWindow()的源代码位于video\SDL_video.c中,如下所示。

SDL_Window * SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags)
{
    SDL_Window *window;
    const char *hint;

    if (!_this) {
        /* Initialize the video system if needed */
        if (SDL_VideoInit(NULL) < 0) {
            return NULL;
        }
    }

    /* Some platforms can't create zero-sized windows */
    if (w < 1) {
        w = 1;
    }
    if (h < 1) {
        h = 1;
    }

    /* Some platforms have OpenGL enabled by default */
#if (SDL_VIDEO_OPENGL && __MACOSX__) || __IPHONEOS__ || __ANDROID__
    flags |= SDL_WINDOW_OPENGL;
#endif
    if (flags & SDL_WINDOW_OPENGL) {
        if (!_this->GL_CreateContext) {
            SDL_SetError("No OpenGL support in video driver");
            return NULL;
        }
        if (SDL_GL_LoadLibrary(NULL) < 0) {
            return NULL;
        }
    }

    /* Unless the user has specified the high-DPI disabling hint, respect the
     * SDL_WINDOW_ALLOW_HIGHDPI flag.
     */
    if (flags & SDL_WINDOW_ALLOW_HIGHDPI) {
        hint = SDL_GetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED);
        if (hint && SDL_atoi(hint) > 0) {
            flags &= ~SDL_WINDOW_ALLOW_HIGHDPI;
        }
    }

    window = (SDL_Window *)SDL_calloc(1, sizeof(*window));
    if (!window) {
        SDL_OutOfMemory();
        return NULL;
    }
    window->magic = &_this->window_magic;
    window->id = _this->next_object_id++;
    window->x = x;
    window->y = y;
    window->w = w;
    window->h = h;
    if (SDL_WINDOWPOS_ISUNDEFINED(x) || SDL_WINDOWPOS_ISUNDEFINED(y) ||
        SDL_WINDOWPOS_ISCENTERED(x) || SDL_WINDOWPOS_ISCENTERED(y)) {
        SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
        int displayIndex;
        SDL_Rect bounds;

        displayIndex = SDL_GetIndexOfDisplay(display);
        SDL_GetDisplayBounds(displayIndex, &bounds);
        if (SDL_WINDOWPOS_ISUNDEFINED(x) || SDL_WINDOWPOS_ISCENTERED(x)) {
            window->x = bounds.x + (bounds.w - w) / 2;
        }
        if (SDL_WINDOWPOS_ISUNDEFINED(y) || SDL_WINDOWPOS_ISCENTERED(y)) {
            window->y = bounds.y + (bounds.h - h) / 2;
        }
    }
    window->flags = ((flags & CREATE_FLAGS) | SDL_WINDOW_HIDDEN);
    window->last_fullscreen_flags = window->flags;
    window->brightness = 1.0f;
    window->next = _this->windows;
    window->is_destroying = SDL_FALSE;

    if (_this->windows) {
        _this->windows->prev = window;
    }
    _this->windows = window;

    if (_this->CreateWindow && _this->CreateWindow(_this, window) < 0) {
        SDL_DestroyWindow(window);
        return NULL;
    }

    if (title) {
        SDL_SetWindowTitle(window, title);
    }
    SDL_FinishWindowCreation(window, flags);

    /* If the window was created fullscreen, make sure the mode code matches */
    SDL_UpdateFullscreenMode(window, FULLSCREEN_VISIBLE(window));

    return window;
}

下面总结一下SDL_CreateWindow()的大致流程。

1. 一些为了保证各个平台的兼容性的初始化工作。各个平台创建窗口的条件不同。例如,某些平台不支持创建大小为0的窗口。再例如,某些平台默认开启OpenGL。

2. 调用SDL_calloc()为SDL_Window结构体分配一块内存。同时设置一些基本属性,例如窗口的宽高,位置等等。

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

#define memset  SDL_memset
#define memcpy  SDL_memcpy
#define malloc  SDL_malloc
#define calloc  SDL_calloc
#define realloc SDL_realloc
#define free    SDL_free

3. 调用VideoDevice的CreateWindow()方法创建窗口。这是创建窗口这个函数中最关键的一环。在这里有一点需要注意,SDL中有一个SDL_VideoDevice类型的静态全局变量_this。SDL调用视频驱动的功能都是通过调用该指针完成的。定义如下。

static SDL_VideoDevice *_this = NULL;

该_this变量代表了当前视频驱动设备。该变量在SDL_Init()中被赋值。如果是Windows下使用,则会被赋值为“Windows视频驱动”;Android下使用,则会被赋值为“Android视频驱动”。这是上篇文章中的内容,不再重复记录。

下面我们以“Windows视频驱动”为例,看看CreateWindow()都会执行哪些函数。

首先回顾一下上篇文章中的一个知识。从上一篇文章的SDL_Init()函数的分析中我们可以得知,Windows视频驱动初始化的时候会给SDL_VideoDevice一系列的函数指针赋值,如下所示。

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;
}

从上文中可以看出,“Windows视频驱动”初始化之后,调用该SDL_VideoDevice的CreateWindow()实际上就等同于调用WIN_CreateWindow()这个函数。因此,我们来看一下WIN_CreateWindow()这个函数的定义(位于video\windows\SDL_windowswindow.c)。

int WIN_CreateWindow(_THIS, SDL_Window * window)
{
    HWND hwnd;
    RECT rect;
    DWORD style = STYLE_BASIC;
    int x, y;
    int w, h;

    style |= GetWindowStyle(window);

    /* Figure out what the window area will be */
    rect.left = window->x;
    rect.top = window->y;
    rect.right = window->x + window->w;
    rect.bottom = window->y + window->h;
    AdjustWindowRectEx(&rect, style, FALSE, 0);
    x = rect.left;
    y = rect.top;
    w = (rect.right - rect.left);
    h = (rect.bottom - rect.top);

    hwnd =
        CreateWindow(SDL_Appname, TEXT(""), style, x, y, w, h, NULL, NULL,
                     SDL_Instance, NULL);
    if (!hwnd) {
        return WIN_SetError("Couldn't create window");
    }

    WIN_PumpEvents(_this);

    if (SetupWindowData(_this, window, hwnd, SDL_TRUE) < 0) {
        DestroyWindow(hwnd);
        return -1;
    }

#if SDL_VIDEO_OPENGL_WGL
    /* We need to initialize the extensions before deciding how to create ES profiles */
    if (window->flags & SDL_WINDOW_OPENGL) {
        WIN_GL_InitExtensions(_this);
    }
#endif

#if SDL_VIDEO_OPENGL_ES2
    if ((window->flags & SDL_WINDOW_OPENGL) &&
        _this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES
#if SDL_VIDEO_OPENGL_WGL
        && (!_this->gl_data || !_this->gl_data->HAS_WGL_EXT_create_context_es2_profile)
#endif
        ) {
#if SDL_VIDEO_OPENGL_EGL
        if (WIN_GLES_SetupWindow(_this, window) < 0) {
            WIN_DestroyWindow(_this, window);
            return -1;
        }
#else
        return SDL_SetError("Could not create GLES window surface (no EGL support available)");
#endif /* SDL_VIDEO_OPENGL_EGL */
    } else
#endif /* SDL_VIDEO_OPENGL_ES2 */

#if SDL_VIDEO_OPENGL_WGL
    if (window->flags & SDL_WINDOW_OPENGL) {
        if (WIN_GL_SetupWindow(_this, window) < 0) {
            WIN_DestroyWindow(_this, window);
            return -1;
        }
    }
#endif

    return 0;
}

从该函数的代码中我们可以看到很多的Win32的API。最核心的函数只有一个,就是CreateWindow()。正是这个Win32的API最终创建了SDL的窗口。当然,为了创建出来的窗口更“优质”,包含了一些初始化的工作,例如AdjustWindowRectEx();以及一些收尾工作,例如SetupWindowData()(该函数主要用于设置SDL_Window的参数)。

4. 完成一些收尾工作。例如设置窗口的标题,如果是“全屏模式”则设置全屏显示等等。在这里简单介绍几个函数。
SDL_SetWindowTitle()用于设置窗口的标题,它的定义如下。

void SDL_SetWindowTitle(SDL_Window * window, const char *title)
{
    CHECK_WINDOW_MAGIC(window, );

    if (title == window->title) {
        return;
    }
    SDL_free(window->title);
    if (title && *title) {
        window->title = SDL_strdup(title);
    } else {
        window->title = NULL;
    }

    if (_this->SetWindowTitle) {
        _this->SetWindowTitle(_this, window);
    }
}

该函数调用了SDL_VideoDevice的SetWindowTitle()。在“Windows视频驱动”中,实际的执行函数是WIN_SetWindowTitle()。该函数的定义如下。

void WIN_SetWindowTitle(_THIS, SDL_Window * window)
{
    HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd;
    LPTSTR title;

    if (window->title) {
        title = WIN_UTF8ToString(window->title);
    } else {
        title = NULL;
    }
    SetWindowText(hwnd, title ? title : TEXT(""));
    SDL_free(title);
}

从代码中可以看出,该函数调用了Win32的API函数SetWindowText()设置窗口的标题。

SDL_FinishWindowCreation()完成一些窗口的收尾工作。该函数的定义如下。

static void SDL_FinishWindowCreation(SDL_Window *window, Uint32 flags)
{
    window->windowed.x = window->x;
    window->windowed.y = window->y;
    window->windowed.w = window->w;
    window->windowed.h = window->h;

    if (flags & SDL_WINDOW_MAXIMIZED) {
        SDL_MaximizeWindow(window);
    }
    if (flags & SDL_WINDOW_MINIMIZED) {
        SDL_MinimizeWindow(window);
    }
    if (flags & SDL_WINDOW_FULLSCREEN) {
        SDL_SetWindowFullscreen(window, flags);
    }
    if (flags & SDL_WINDOW_INPUT_GRABBED) {
        SDL_SetWindowGrab(window, SDL_TRUE);
    }
    if (!(flags & SDL_WINDOW_HIDDEN)) {
        SDL_ShowWindow(window);
    }
}

从代码中可以看出,如果创建窗口的时候:

指定了“最大化”,则会执行SDL_MaximizeWindow();

指定了“最小化”,则会执行SDL_MinimizeWindow();

指定了“全屏”,则会执行SDL_SetWindowFullscreen();

指定了“抓取”(这个没有试过),则会执行SDL_SetWindowGrab();

指定了“隐藏”,则会执行SDL_ShowWindow()。

下面分别看一下SDL_MaximizeWindow(),SDL_MinimizeWindow(),SDL_SetWindowFullscreen(),SDL_ShowWindow()的代码。


SDL_MaximizeWindow()定义如下。

void SDL_MaximizeWindow(SDL_Window * window)
{
    CHECK_WINDOW_MAGIC(window, );

    if (window->flags & SDL_WINDOW_MAXIMIZED) {
        return;
    }

    /* !!! FIXME: should this check if the window is resizable? */

    if (_this->MaximizeWindow) {
        _this->MaximizeWindow(_this, window);
    }
}

从代码中可以看出,SDL_MaximizeWindow()调用了SDL_VideoDevice的MaximizeWindow()函数。在“Windows视频驱动”下,实际上调用了WIN_MaximizeWindow()函数,该函数的定义如下。

void WIN_MaximizeWindow(_THIS, SDL_Window * window)
{
    SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
    HWND hwnd = data->hwnd;
    data->expected_resize = TRUE;
    ShowWindow(hwnd, SW_MAXIMIZE);
    data->expected_resize = FALSE;
}

从上述代码中可以看出WIN_MaximizeWindow()调用了Win32的API函数ShowWindow()。


SDL_MinimizeWindow()定义如下。

void SDL_MinimizeWindow(SDL_Window * window)
{
    CHECK_WINDOW_MAGIC(window, );

    if (window->flags & SDL_WINDOW_MINIMIZED) {
        return;
    }

    SDL_UpdateFullscreenMode(window, SDL_FALSE);

    if (_this->MinimizeWindow) {
        _this->MinimizeWindow(_this, window);
    }
}

从代码中可以看出,SDL_MinimizeWindow()调用了SDL_VideoDevice的MinimizeWindow()函数。在“Windows视频驱动”下,实际上调用了WIN_MinimizeWindow()函数,该函数的定义如下。

void WIN_MinimizeWindow(_THIS, SDL_Window * window)
{
    HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd;
    ShowWindow(hwnd, SW_MINIMIZE);
}

从上述代码中可以看出WIN_MinimizeWindow()调用了Win32的API函数ShowWindow()。

SDL_SetWindowFullscreen()定义如下。

int SDL_SetWindowFullscreen(SDL_Window * window, Uint32 flags)
{
    CHECK_WINDOW_MAGIC(window, -1);

    flags &= FULLSCREEN_MASK;

    if ( flags == (window->flags & FULLSCREEN_MASK) ) {
        return 0;
    }

    /* clear the previous flags and OR in the new ones */
    window->flags &= ~FULLSCREEN_MASK;
    window->flags |= flags;

    SDL_UpdateFullscreenMode(window, FULLSCREEN_VISIBLE(window));

    return 0;
}

从代码中可以看出,SDL_SetWindowFullscreen()调用了SDL_UpdateFullscreenMode()函数,该函数的定义如下。

static void SDL_UpdateFullscreenMode(SDL_Window * window, SDL_bool fullscreen)
{
    SDL_VideoDisplay *display;
    SDL_Window *other;

#ifdef __MACOSX__
    if (Cocoa_SetWindowFullscreenSpace(window, fullscreen)) {
        window->last_fullscreen_flags = window->flags;
        return;
    }
#endif

    display = SDL_GetDisplayForWindow(window);

    if (fullscreen) {
        /* Hide any other fullscreen windows */
        if (display->fullscreen_window &&
            display->fullscreen_window != window) {
            SDL_MinimizeWindow(display->fullscreen_window);
        }
    }

    /* See if anything needs to be done now */
    if ((display->fullscreen_window == window) == fullscreen) {
        if ((window->last_fullscreen_flags & FULLSCREEN_MASK) == (window->flags & FULLSCREEN_MASK)) {
            return;
        }
    }

    /* See if there are any fullscreen windows */
    for (other = _this->windows; other; other = other->next) {
        SDL_bool setDisplayMode = SDL_FALSE;

        if (other == window) {
            setDisplayMode = fullscreen;
        } else if (FULLSCREEN_VISIBLE(other) &&
                   SDL_GetDisplayForWindow(other) == display) {
            setDisplayMode = SDL_TRUE;
        }

        if (setDisplayMode) {
            SDL_DisplayMode fullscreen_mode;

            if (SDL_GetWindowDisplayMode(other, &fullscreen_mode) == 0) {
                SDL_bool resized = SDL_TRUE;

                if (other->w == fullscreen_mode.w && other->h == fullscreen_mode.h) {
                    resized = SDL_FALSE;
                }

                /* only do the mode change if we want exclusive fullscreen */
                if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP) {
                    SDL_SetDisplayModeForDisplay(display, &fullscreen_mode);
                } else {
                    SDL_SetDisplayModeForDisplay(display, NULL);
                }

                if (_this->SetWindowFullscreen) {
                    _this->SetWindowFullscreen(_this, other, display, SDL_TRUE);
                }
                display->fullscreen_window = other;

                /* Generate a mode change event here */
                if (resized) {
                    SDL_SendWindowEvent(other, SDL_WINDOWEVENT_RESIZED,
                                        fullscreen_mode.w, fullscreen_mode.h);
                } else {
                    SDL_OnWindowResized(other);
                }

                SDL_RestoreMousePosition(other);

                window->last_fullscreen_flags = window->flags;
                return;
            }
        }
    }

    /* Nope, restore the desktop mode */
    SDL_SetDisplayModeForDisplay(display, NULL);

    if (_this->SetWindowFullscreen) {
        _this->SetWindowFullscreen(_this, window, display, SDL_FALSE);
    }
    display->fullscreen_window = NULL;

    /* Generate a mode change event here */
    SDL_OnWindowResized(window);

    /* Restore the cursor position */
    SDL_RestoreMousePosition(window);

    window->last_fullscreen_flags = window->flags;
}

SDL_UpdateFullscreenMode()代码很长,在这里我们只选择最关键的代码进行分析。SDL_UpdateFullscreenMode()最关键的地方在于它调用了SDL_VideoDevice的SetWindowFullscreen()函数。在“Windows视频驱动”下,实际上调用了WIN_SetWindowFullscreen()函数,该函数的定义如下。

void WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
{
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    HWND hwnd = data->hwnd;
    RECT rect;
    SDL_Rect bounds;
    DWORD style;
    HWND top;
    BOOL menu;
    int x, y;
    int w, h;

    if (SDL_ShouldAllowTopmost() && (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_INPUT_FOCUS)) == (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_INPUT_FOCUS)) {
        top = HWND_TOPMOST;
    } else {
        top = HWND_NOTOPMOST;
    }

    style = GetWindowLong(hwnd, GWL_STYLE);
    style &= ~STYLE_MASK;
    style |= GetWindowStyle(window);

    WIN_GetDisplayBounds(_this, display, &bounds);

    if (fullscreen) {
        x = bounds.x;
        y = bounds.y;
        w = bounds.w;
        h = bounds.h;
    } else {
        rect.left = 0;
        rect.top = 0;
        rect.right = window->windowed.w;
        rect.bottom = window->windowed.h;
        menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL);
        AdjustWindowRectEx(&rect, style, menu, 0);
        w = (rect.right - rect.left);
        h = (rect.bottom - rect.top);
        x = window->windowed.x + rect.left;
        y = window->windowed.y + rect.top;
    }
    SetWindowLong(hwnd, GWL_STYLE, style);
    data->expected_resize = TRUE;
    SetWindowPos(hwnd, top, x, y, w, h, SWP_NOCOPYBITS | SWP_NOACTIVATE);
    data->expected_resize = FALSE;
}

从代码中可以看出,该函数通过WIN_GetDisplayBounds()获得屏幕的尺寸,然后通过SetWindowPos()函数设置全屏窗口的大小和位置。

SDL_ShowWindow()的定义如下。

void SDL_ShowWindow(SDL_Window * window)
{
    CHECK_WINDOW_MAGIC(window, );

    if (window->flags & SDL_WINDOW_SHOWN) {
        return;
    }

    if (_this->ShowWindow) {
        _this->ShowWindow(_this, window);
    }
    SDL_SendWindowEvent(window, SDL_WINDOWEVENT_SHOWN, 0, 0);
}

SDL_ShowWindow ()调用了SDL_VideoDevice的ShowWindow()函数。在“Windows视频驱动”下,实际上调用了WIN_ShowWindow()函数,该函数的定义如下。

void WIN_ShowWindow(_THIS, SDL_Window * window)
{
    HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd;
    ShowWindow(hwnd, SW_SHOW);
}

该函数比较简单,直接调用了Win32中的ShowWindow()方法。

SDL2源代码分析2:窗口(SDL_Window)的更多相关文章

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

    ===================================================== SDL源代码分析系列文章列表: SDL2源代码分析1:初始化(SDL_Init()) SDL ...

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

    ===================================================== SDL源代码分析系列文章列表: SDL2源代码分析1:初始化(SDL_Init()) SDL ...

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

    ===================================================== SDL源代码分析系列文章列表: SDL2源代码分析1:初始化(SDL_Init()) SDL ...

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

    ===================================================== SDL源代码分析系列文章列表: SDL2源代码分析1:初始化(SDL_Init()) SDL ...

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

    ===================================================== SDL源代码分析系列文章列表: SDL2源代码分析1:初始化(SDL_Init()) SDL ...

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

    ===================================================== SDL源代码分析系列文章列表: SDL2源代码分析1:初始化(SDL_Init()) SDL ...

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

    ===================================================== SDL源代码分析系列文章列表: SDL2源代码分析1:初始化(SDL_Init()) SDL ...

  8. 转:SDL2源代码分析

    1:初始化(SDL_Init()) SDL简介 有关SDL的简介在<最简单的视音频播放示例7:SDL2播放RGB/YUV>以及<最简单的视音频播放示例9:SDL2播放PCM>中 ...

  9. SDL2源代码分析

    1:初始化(SDL_Init()) SDL简介 有关SDL的简介在<最简单的视音频播放示例7:SDL2播放RGB/YUV>以及<最简单的视音频播放示例9:SDL2播放PCM>中 ...

随机推荐

  1. JFinal实现伪静态

    JFinal 是基于 Java 语言的极速 WEB + ORM 框架,其核心设计目标是开发迅速.代码量少.学习简单.功能强大.轻量级.易扩展.Restful.在拥有Java语言所有优势的同时再拥有ru ...

  2. Spring中整合Cage,实现验证码功能

    1.pom.xml中添加Cage依赖. <dependency> <groupId>com.github.cage</groupId> <artifactId ...

  3. Tomcat出现validateJarFile-jar not loaded问题

    tomcat启动时问题: validateJarFile(...\WEB-INF\lib\servlet-api.jar)-jar not loaded. See Servlet Spec 2.3, ...

  4. 关于ES7中的async/await在客户端和服务端上的实践

    一.前言 在项目中经常遇到处理异步请求的情况,面对层层的嵌套,回调显示那么苍白无力: async / await是ES7的重要特性之一,也是目前社区里公认的优秀异步解决方案,既然这样就用上吧. 二.配 ...

  5. 关于mysql安装到最后一步老是停留在starting server,显示无响应

    从昨天晚上到今天安装MySQL花了好长的时间,一直是在后面starting server 这部就显示无响应,查资料了解到是MySQL有残留,有些注册表文件需要手动清理,下面是具体方法. 1.先用卸载软 ...

  6. Go 语言 goto 语句

    Go 语言的 goto 语句可以无条件地转移到过程中指定的行. goto语句通常与条件语句配合使用.可用来实现条件转移, 构成循环,跳出循环体等功能. 但是,在结构化程序设计中一般不主张使用goto语 ...

  7. 工作流引擎 Flowable 6.0.0.RC1 release,完全兼容Activi

    Flowable 6.0.0.RC1 release,第一个可流动的6引擎版本(6.0.0.RC1). Flowable 6.0.0.RC1 relase新增加的功能以及特色: 包重命名为org.Fl ...

  8. springMVC源码分析--ModelFactory

    ModelFactory是用来维护Model的,具体包含两个功能 (1)初始化Model (2)处理器执行后将Model中相应的参数更新到SessionAttributes中 1.初始化Model其实 ...

  9. Mybatis 批量插入、批量更新

            合理的使用批量插入.更新对性能优化有很大的作用,速度明显快了N倍.         要注意数据库连接串后面要新增:&allowMultiQueries=true,表示一个sql ...

  10. 有一个排序二叉树,数据类型是int型,如何找出中间大的元素。

    void tree2Dll(TNode* root, TNode*& tail) { if (!root) { return; } if (root->left) { tree2Dll( ...