src/video/raspberry/SDL_rpivideo.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 24 Sep 2018 11:49:25 -0700
changeset 12201 8bdc4d340419
parent 11853 c7946b873fe8
child 12503 806492103856
permissions -rw-r--r--
Fixed whitespace
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 
    22 #include "../../SDL_internal.h"
    23 
    24 #if SDL_VIDEO_DRIVER_RPI
    25 
    26 /* References
    27  * http://elinux.org/RPi_VideoCore_APIs
    28  * https://github.com/raspberrypi/firmware/blob/master/opt/vc/src/hello_pi/hello_triangle/triangle.c
    29  * http://cgit.freedesktop.org/wayland/weston/tree/src/rpi-renderer.c
    30  * http://cgit.freedesktop.org/wayland/weston/tree/src/compositor-rpi.c
    31  */
    32 
    33 /* SDL internals */
    34 #include "../SDL_sysvideo.h"
    35 #include "SDL_version.h"
    36 #include "SDL_syswm.h"
    37 #include "SDL_loadso.h"
    38 #include "SDL_events.h"
    39 #include "../../events/SDL_mouse_c.h"
    40 #include "../../events/SDL_keyboard_c.h"
    41 #include "SDL_hints.h"
    42 
    43 #ifdef SDL_INPUT_LINUXEV
    44 #include "../../core/linux/SDL_evdev.h"
    45 #endif
    46 
    47 /* RPI declarations */
    48 #include "SDL_rpivideo.h"
    49 #include "SDL_rpievents_c.h"
    50 #include "SDL_rpiopengles.h"
    51 #include "SDL_rpimouse.h"
    52 
    53 static int
    54 RPI_Available(void)
    55 {
    56     return 1;
    57 }
    58 
    59 static void
    60 RPI_Destroy(SDL_VideoDevice * device)
    61 {
    62     SDL_free(device->driverdata);
    63     SDL_free(device);
    64 }
    65 
    66 static int 
    67 RPI_GetRefreshRate()
    68 {
    69     TV_DISPLAY_STATE_T tvstate;
    70     if (vc_tv_get_display_state( &tvstate ) == 0) {
    71         //The width/height parameters are in the same position in the union
    72         //for HDMI and SDTV
    73         HDMI_PROPERTY_PARAM_T property;
    74         property.property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE;
    75         vc_tv_hdmi_get_property(&property);
    76         return property.param1 == HDMI_PIXEL_CLOCK_TYPE_NTSC ? 
    77             tvstate.display.hdmi.frame_rate * (1000.0f/1001.0f) : 
    78             tvstate.display.hdmi.frame_rate;
    79     } 
    80     return 60;  /* Failed to get display state, default to 60 */
    81 }
    82 
    83 static SDL_VideoDevice *
    84 RPI_Create()
    85 {
    86     SDL_VideoDevice *device;
    87     SDL_VideoData *phdata;
    88 
    89     /* Initialize SDL_VideoDevice structure */
    90     device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
    91     if (device == NULL) {
    92         SDL_OutOfMemory();
    93         return NULL;
    94     }
    95 
    96     /* Initialize internal data */
    97     phdata = (SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
    98     if (phdata == NULL) {
    99         SDL_OutOfMemory();
   100         SDL_free(device);
   101         return NULL;
   102     }
   103 
   104     device->driverdata = phdata;
   105 
   106     /* Setup amount of available displays */
   107     device->num_displays = 0;
   108 
   109     /* Set device free function */
   110     device->free = RPI_Destroy;
   111 
   112     /* Setup all functions which we can handle */
   113     device->VideoInit = RPI_VideoInit;
   114     device->VideoQuit = RPI_VideoQuit;
   115     device->GetDisplayModes = RPI_GetDisplayModes;
   116     device->SetDisplayMode = RPI_SetDisplayMode;
   117     device->CreateSDLWindow = RPI_CreateWindow;
   118     device->CreateSDLWindowFrom = RPI_CreateWindowFrom;
   119     device->SetWindowTitle = RPI_SetWindowTitle;
   120     device->SetWindowIcon = RPI_SetWindowIcon;
   121     device->SetWindowPosition = RPI_SetWindowPosition;
   122     device->SetWindowSize = RPI_SetWindowSize;
   123     device->ShowWindow = RPI_ShowWindow;
   124     device->HideWindow = RPI_HideWindow;
   125     device->RaiseWindow = RPI_RaiseWindow;
   126     device->MaximizeWindow = RPI_MaximizeWindow;
   127     device->MinimizeWindow = RPI_MinimizeWindow;
   128     device->RestoreWindow = RPI_RestoreWindow;
   129     device->SetWindowGrab = RPI_SetWindowGrab;
   130     device->DestroyWindow = RPI_DestroyWindow;
   131 #if 0
   132     device->GetWindowWMInfo = RPI_GetWindowWMInfo;
   133 #endif
   134     device->GL_LoadLibrary = RPI_GLES_LoadLibrary;
   135     device->GL_GetProcAddress = RPI_GLES_GetProcAddress;
   136     device->GL_UnloadLibrary = RPI_GLES_UnloadLibrary;
   137     device->GL_CreateContext = RPI_GLES_CreateContext;
   138     device->GL_MakeCurrent = RPI_GLES_MakeCurrent;
   139     device->GL_SetSwapInterval = RPI_GLES_SetSwapInterval;
   140     device->GL_GetSwapInterval = RPI_GLES_GetSwapInterval;
   141     device->GL_SwapWindow = RPI_GLES_SwapWindow;
   142     device->GL_DeleteContext = RPI_GLES_DeleteContext;
   143     device->GL_DefaultProfileConfig = RPI_GLES_DefaultProfileConfig;
   144 
   145     device->PumpEvents = RPI_PumpEvents;
   146 
   147     return device;
   148 }
   149 
   150 VideoBootStrap RPI_bootstrap = {
   151     "RPI",
   152     "RPI Video Driver",
   153     RPI_Available,
   154     RPI_Create
   155 };
   156 
   157 /*****************************************************************************/
   158 /* SDL Video and Display initialization/handling functions                   */
   159 /*****************************************************************************/
   160 int
   161 RPI_VideoInit(_THIS)
   162 {
   163     SDL_VideoDisplay display;
   164     SDL_DisplayMode current_mode;
   165     SDL_DisplayData *data;
   166     uint32_t w,h;
   167 
   168     /* Initialize BCM Host */
   169     bcm_host_init();
   170 
   171     SDL_zero(current_mode);
   172 
   173     if (graphics_get_display_size( 0, &w, &h) < 0) {
   174         return -1;
   175     }
   176 
   177     current_mode.w = w;
   178     current_mode.h = h;
   179     current_mode.refresh_rate = RPI_GetRefreshRate();
   180     /* 32 bpp for default */
   181     current_mode.format = SDL_PIXELFORMAT_ABGR8888;
   182 
   183     current_mode.driverdata = NULL;
   184 
   185     SDL_zero(display);
   186     display.desktop_mode = current_mode;
   187     display.current_mode = current_mode;
   188 
   189     /* Allocate display internal data */
   190     data = (SDL_DisplayData *) SDL_calloc(1, sizeof(SDL_DisplayData));
   191     if (data == NULL) {
   192         return SDL_OutOfMemory();
   193     }
   194 
   195     data->dispman_display = vc_dispmanx_display_open( 0 /* LCD */);
   196 
   197     display.driverdata = data;
   198 
   199     SDL_AddVideoDisplay(&display);
   200 
   201 #ifdef SDL_INPUT_LINUXEV    
   202     if (SDL_EVDEV_Init() < 0) {
   203         return -1;
   204     }
   205 #endif    
   206     
   207     RPI_InitMouse(_this);
   208 
   209     return 1;
   210 }
   211 
   212 void
   213 RPI_VideoQuit(_THIS)
   214 {
   215 #ifdef SDL_INPUT_LINUXEV    
   216     SDL_EVDEV_Quit();
   217 #endif    
   218 }
   219 
   220 void
   221 RPI_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
   222 {
   223     /* Only one display mode available, the current one */
   224     SDL_AddDisplayMode(display, &display->current_mode);
   225 }
   226 
   227 int
   228 RPI_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
   229 {
   230     return 0;
   231 }
   232 
   233 static void
   234 RPI_vsync_callback(DISPMANX_UPDATE_HANDLE_T u, void *data)
   235 {
   236    SDL_WindowData *wdata = ((SDL_WindowData *) data);
   237 
   238    SDL_LockMutex(wdata->vsync_cond_mutex);
   239    SDL_CondSignal(wdata->vsync_cond);
   240    SDL_UnlockMutex(wdata->vsync_cond_mutex);
   241 }
   242 
   243 int
   244 RPI_CreateWindow(_THIS, SDL_Window * window)
   245 {
   246     SDL_WindowData *wdata;
   247     SDL_VideoDisplay *display;
   248     SDL_DisplayData *displaydata;
   249     VC_RECT_T dst_rect;
   250     VC_RECT_T src_rect;
   251     VC_DISPMANX_ALPHA_T         dispman_alpha;
   252     DISPMANX_UPDATE_HANDLE_T dispman_update;
   253     uint32_t layer = SDL_RPI_VIDEOLAYER;
   254     const char *env;
   255 
   256     /* Disable alpha, otherwise the app looks composed with whatever dispman is showing (X11, console,etc) */
   257     dispman_alpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS; 
   258     dispman_alpha.opacity = 0xFF; 
   259     dispman_alpha.mask = 0;
   260 
   261     /* Allocate window internal data */
   262     wdata = (SDL_WindowData *) SDL_calloc(1, sizeof(SDL_WindowData));
   263     if (wdata == NULL) {
   264         return SDL_OutOfMemory();
   265     }
   266     display = SDL_GetDisplayForWindow(window);
   267     displaydata = (SDL_DisplayData *) display->driverdata;
   268 
   269     /* Windows have one size for now */
   270     window->w = display->desktop_mode.w;
   271     window->h = display->desktop_mode.h;
   272 
   273     /* OpenGL ES is the law here, buddy */
   274     window->flags |= SDL_WINDOW_OPENGL;
   275 
   276     /* Create a dispman element and associate a window to it */
   277     dst_rect.x = 0;
   278     dst_rect.y = 0;
   279     dst_rect.width = window->w;
   280     dst_rect.height = window->h;
   281 
   282     src_rect.x = 0;
   283     src_rect.y = 0;
   284     src_rect.width = window->w << 16;
   285     src_rect.height = window->h << 16;
   286 
   287     env = SDL_GetHint(SDL_HINT_RPI_VIDEO_LAYER);
   288     if (env) {
   289         layer = SDL_atoi(env);
   290     }
   291 
   292     dispman_update = vc_dispmanx_update_start( 0 );
   293     wdata->dispman_window.element = vc_dispmanx_element_add (dispman_update,
   294                                                              displaydata->dispman_display,
   295                                                              layer /* layer */,
   296                                                              &dst_rect,
   297                                                              0 /*src*/,
   298                                                              &src_rect,
   299                                                              DISPMANX_PROTECTION_NONE,
   300                                                              &dispman_alpha /*alpha*/,
   301                                                              0 /*clamp*/,
   302                                                              0 /*transform*/);
   303     wdata->dispman_window.width = window->w;
   304     wdata->dispman_window.height = window->h;
   305     vc_dispmanx_update_submit_sync(dispman_update);
   306     
   307     if (!_this->egl_data) {
   308         if (SDL_GL_LoadLibrary(NULL) < 0) {
   309             return -1;
   310         }
   311     }
   312     wdata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) &wdata->dispman_window);
   313 
   314     if (wdata->egl_surface == EGL_NO_SURFACE) {
   315         return SDL_SetError("Could not create GLES window surface");
   316     }
   317 
   318     /* Start generating vsync callbacks if necesary */
   319     wdata->double_buffer = SDL_FALSE;
   320     if (SDL_GetHintBoolean(SDL_HINT_VIDEO_DOUBLE_BUFFER, SDL_FALSE)) {
   321         wdata->vsync_cond = SDL_CreateCond();
   322         wdata->vsync_cond_mutex = SDL_CreateMutex();
   323         wdata->double_buffer = SDL_TRUE;
   324         vc_dispmanx_vsync_callback(displaydata->dispman_display, RPI_vsync_callback, (void*)wdata);
   325     }
   326 
   327     /* Setup driver data for this window */
   328     window->driverdata = wdata;
   329 
   330     /* One window, it always has focus */
   331     SDL_SetMouseFocus(window);
   332     SDL_SetKeyboardFocus(window);
   333 
   334     /* Window has been successfully created */
   335     return 0;
   336 }
   337 
   338 void
   339 RPI_DestroyWindow(_THIS, SDL_Window * window)
   340 {
   341     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   342     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
   343     SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
   344 
   345     if(data) {
   346         if (data->double_buffer) {
   347             /* Wait for vsync, and then stop vsync callbacks and destroy related stuff, if needed */
   348             SDL_LockMutex(data->vsync_cond_mutex);
   349             SDL_CondWait(data->vsync_cond, data->vsync_cond_mutex);
   350             SDL_UnlockMutex(data->vsync_cond_mutex);
   351 
   352             vc_dispmanx_vsync_callback(displaydata->dispman_display, NULL, NULL);
   353 
   354             SDL_DestroyCond(data->vsync_cond);
   355             SDL_DestroyMutex(data->vsync_cond_mutex);
   356         }
   357 
   358 #if SDL_VIDEO_OPENGL_EGL
   359         if (data->egl_surface != EGL_NO_SURFACE) {
   360             SDL_EGL_DestroySurface(_this, data->egl_surface);
   361         }
   362 #endif
   363         SDL_free(data);
   364         window->driverdata = NULL;
   365     }
   366 }
   367 
   368 int
   369 RPI_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
   370 {
   371     return -1;
   372 }
   373 
   374 void
   375 RPI_SetWindowTitle(_THIS, SDL_Window * window)
   376 {
   377 }
   378 void
   379 RPI_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
   380 {
   381 }
   382 void
   383 RPI_SetWindowPosition(_THIS, SDL_Window * window)
   384 {
   385 }
   386 void
   387 RPI_SetWindowSize(_THIS, SDL_Window * window)
   388 {
   389 }
   390 void
   391 RPI_ShowWindow(_THIS, SDL_Window * window)
   392 {
   393 }
   394 void
   395 RPI_HideWindow(_THIS, SDL_Window * window)
   396 {
   397 }
   398 void
   399 RPI_RaiseWindow(_THIS, SDL_Window * window)
   400 {
   401 }
   402 void
   403 RPI_MaximizeWindow(_THIS, SDL_Window * window)
   404 {
   405 }
   406 void
   407 RPI_MinimizeWindow(_THIS, SDL_Window * window)
   408 {
   409 }
   410 void
   411 RPI_RestoreWindow(_THIS, SDL_Window * window)
   412 {
   413 }
   414 void
   415 RPI_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
   416 {
   417 
   418 }
   419 
   420 /*****************************************************************************/
   421 /* SDL Window Manager function                                               */
   422 /*****************************************************************************/
   423 #if 0
   424 SDL_bool
   425 RPI_GetWindowWMInfo(_THIS, SDL_Window * window, struct SDL_SysWMinfo *info)
   426 {
   427     if (info->version.major <= SDL_MAJOR_VERSION) {
   428         return SDL_TRUE;
   429     } else {
   430         SDL_SetError("application not compiled with SDL %d.%d",
   431                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
   432         return SDL_FALSE;
   433     }
   434 
   435     /* Failed to get window manager information */
   436     return SDL_FALSE;
   437 }
   438 #endif
   439 
   440 #endif /* SDL_VIDEO_DRIVER_RPI */
   441 
   442 /* vi: set ts=4 sw=4 expandtab: */