SDL2.0窗口创建历程

来源:互联网 发布:ubuntu 14.04 32下载 编辑:程序博客网 时间:2024/06/01 15:45

SDL2.0窗口创建历程

本文简单记录一下SDL2.0在Android平台的窗口创建历程,分析Android是怎么将SurfaceView转化为SDL中的窗口:

首先,先看一下SDL的初始化调用流程:

Created with Raphaël 2.1.0SDL_Init()SDL_InitSubSystem()SDL_VideoInit()VideoBootStrapAndroid_bootstrap->create()

最终通过JNI调用Android本地方法ANativeWindow_fromSurface()将SurfaceView转换为SDL可以使用的ANativeWindow对象

Created with Raphaël 2.1.0create()Android_CreateDevice()Android_CreateWindow()Android_JNI_GetNativeWindow()ANativeWindow_fromSurface()

下面结合代码逐一说明,初始化函数位于SDL.c中,它的定义如下:

int SDL_Init(Uint32 flags){    return SDL_InitSubSystem(flags);}

SDL将初始化工作分模块进行,参数flags代表着我们要初始化的模块,在SDL.h中定义了这些常量,比如我们可以单独初始化视频(SDL_INIT_VIDEO),也可以全部模块(SDL_INIT_EVERYTHING)
除非用到了所有模块,否则不建议初始化全部模块。

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    }    /* 初始化video子系统 */    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);}

首先判断是否定义了main方法,接着根据初始化标志flags初始化指定模块,播放视频需要初始化video模块,接下来看函数SDL_VideoInit,它位于video/SDL_video.c 中。

int SDL_VideoInit(const char *driver_name){    SDL_VideoDevice *video;    const char *hint;    int index;    int i;    SDL_bool allow_screensaver;    // 确保_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;    }    /* 选择平台对应的video驱动 */    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();    /* 初始化video模块*/    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;}

bootstrap[]数组体现了SDL的跨平台特性,将不同平台的视频驱动函数存放到VideoBootStrap 类型数组中,在编译预处理时,根据平台信息选择编译对应平台的驱动函数

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

VideoBootStrap 声明位于video/SDL_sysvideo.h中,create指向了一个可以创建SDL_VideoDevice的函数,Android平台为Android_CreateDevice

typedef struct VideoBootStrap{    const char *name;    const char *desc;    int (*available) (void);    SDL_VideoDevice *(*create) (int devindex);} VideoBootStrap;

该函数位于video/android/SDL_androidvideo.c中,里面指定了初始化、创建、销毁窗口等函数,其调用Android_VideoInit函数完成初始化工作。

static SDL_VideoDevice *Android_CreateDevice(int devindex){    SDL_VideoDevice *device;    SDL_VideoData *data;    /* 分配空间*/    device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));    if (!device) {        SDL_OutOfMemory();        return NULL;    }    data = (SDL_VideoData*) SDL_calloc(1, sizeof(SDL_VideoData));    if (!data) {        SDL_OutOfMemory();        SDL_free(device);        return NULL;    }    device->driverdata = data;    /* 设置函数指针*/    device->VideoInit = Android_VideoInit;    device->VideoQuit = Android_VideoQuit;    device->PumpEvents = Android_PumpEvents;    device->CreateWindow = Android_CreateWindow;    device->SetWindowTitle = Android_SetWindowTitle;    device->DestroyWindow = Android_DestroyWindow;    device->free = Android_DeleteDevice;    /* OpenGL相关的函数指针 */    device->GL_LoadLibrary = Android_GLES_LoadLibrary;    device->GL_GetProcAddress = Android_GLES_GetProcAddress;    device->GL_UnloadLibrary = Android_GLES_UnloadLibrary;    device->GL_CreateContext = Android_GLES_CreateContext;    device->GL_MakeCurrent = Android_GLES_MakeCurrent;    device->GL_SetSwapInterval = Android_GLES_SetSwapInterval;    device->GL_GetSwapInterval = Android_GLES_GetSwapInterval;    device->GL_SwapWindow = Android_GLES_SwapWindow;    device->GL_DeleteContext = Android_GLES_DeleteContext;    /* Text input */    device->StartTextInput = Android_StartTextInput;    device->StopTextInput = Android_StopTextInput;    device->SetTextInputRect = Android_SetTextInputRect;    /* Screen keyboard */    device->HasScreenKeyboardSupport = Android_HasScreenKeyboardSupport;    device->IsScreenKeyboardShown = Android_IsScreenKeyboardShown;    /* Clipboard */    device->SetClipboardText = Android_SetClipboardText;    device->GetClipboardText = Android_GetClipboardText;    device->HasClipboardText = Android_HasClipboardText;    return device;}

初始化完成后,可以通过上面SDL_VideoDevice中的函数指针CreateWindow创建窗口,接下来看SDL是在何处调用的:

int SDL_CreateWindowAndRenderer(int width, int height, Uint32 window_flags, SDL_Window **window, SDL_Renderer **renderer){    *window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED,                                     SDL_WINDOWPOS_UNDEFINED,                                     width, height, window_flags);    if (!*window) {        *renderer = NULL;        return -1;    }    *renderer = SDL_CreateRenderer(*window, -1, 0);    if (!*renderer) {        return -1;    }    return 0;}

首先在render/SDL_render.c中提供了接口方法SDL_CreateWindowAndRenderer,调用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;}

_this->CreateWindow 调用了Android_CreateWindow,位于 video/android/SDL_androidwindow.c 中

Android_CreateWindow(_THIS, SDL_Window * window){    SDL_WindowData *data;    if (Android_Window) {        return SDL_SetError("Android only supports one window");    }    Android_PauseSem = SDL_CreateSemaphore(0);    Android_ResumeSem = SDL_CreateSemaphore(0);    /* Adjust the window data to match the screen */    window->x = 0;    window->y = 0;    window->w = Android_ScreenWidth;    window->h = Android_ScreenHeight;    window->flags &= ~SDL_WINDOW_RESIZABLE;     /* window is NEVER resizeable */    window->flags |= SDL_WINDOW_FULLSCREEN;     /* window is always fullscreen */    window->flags &= ~SDL_WINDOW_HIDDEN;    window->flags |= SDL_WINDOW_SHOWN;          /* only one window on Android */    window->flags |= SDL_WINDOW_INPUT_FOCUS;    /* always has input focus */    /* One window, it always has focus */    SDL_SetMouseFocus(window);    SDL_SetKeyboardFocus(window);    data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data));    if (!data) {        return SDL_OutOfMemory();    }    data->native_window = Android_JNI_GetNativeWindow();    if (!data->native_window) {        SDL_free(data);        return SDL_SetError("Could not fetch native window");    }    data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) data->native_window);    if (data->egl_surface == EGL_NO_SURFACE) {        ANativeWindow_release(data->native_window);        SDL_free(data);        return SDL_SetError("Could not create GLES window surface");    }    window->driverdata = data;    Android_Window = window;    return 0;}

此函数调用Android_JNI_GetNativeWindow实现surface转换为ANativeWindow,该函数在core/android/SDL_android.c中

ANativeWindow* Android_JNI_GetNativeWindow(void){    ANativeWindow* anw;    jobject s;    JNIEnv *env = Android_JNI_GetEnv();    s = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetNativeSurface);    anw = ANativeWindow_fromSurface(env, s);    (*env)->DeleteLocalRef(env, s);    return anw;}

所以SDL的窗口其实就是将Android的ANativeWindow进一步的封装而成的,至此SDL创建过程就简单记录完毕,第一次写博客,好紧张哦……^ - ^

原创粉丝点击