SDL2源碼分析2:窗體(SDL_Window)

=====================================================
windows

SDL源碼分析系列文章列表:less

SDL2源碼分析1:初始化(SDL_Init())ide

SDL2源碼分析2:窗體(SDL_Window)
函數

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

SDL2源碼分析4:紋理(SDL_Texture)post

SDL2源碼分析5:更新紋理(SDL_UpdateTexture())ui

SDL2源碼分析6:拷貝到渲染器(SDL_RenderCopy())this

SDL2源碼分析7:顯示(SDL_RenderPresent())
spa

SDL2源碼分析8:視頻顯示總結
.net

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


上一篇文章分析了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;

在源碼project中可以看到它的定義。位於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()方法。
相關文章
相關標籤/搜索