• SDL2源码分析2:窗体(SDL_Window)


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

    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的话。是看不到它的内部结构的。有关它的定义在头文件里仅仅有一行代码。可是这一行定义前面的凝视非常之多。例如以下所看到的:

    /**
     *  rief 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中能够看到它的定义。位于videoSDL_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()的源码位于videoSDL_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()。它们的定义位于stdlibSDL_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()这个函数的定义(位于videowindowsSDL_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()方法。


  • 相关阅读:
    随便瞎说
    第六次作业
    第五次作业
    第四次作业
    第二次作业
    第一周java心得
    Ext.Net学习笔记之动态加载TabPanel
    Ext.net学习笔记之ViewPort布局
    MyEclipse连接mySql数据库笔记
    配置服务器
  • 原文地址:https://www.cnblogs.com/zsychanpin/p/7095203.html
Copyright © 2020-2023  润新知