src/video/raspberry/SDL_rpivideo.c
author Ozkan Sezer <sezeroz@gmail.com>
Mon, 21 Oct 2019 22:22:28 +0300
changeset 13155 faed1bb24abb
parent 12615 6f0eb21dd7d1
permissions -rw-r--r--
SDL_endian.h: Use endian.h for OpenBSD.

Patch from OpenBSD CVS, authored by Donovan Watteau.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2019 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 /*****************************************************************************/
   159 /* SDL Video and Display initialization/handling functions                   */
   160 /*****************************************************************************/
   161 
   162 static void
   163 AddDispManXDisplay(const int display_id)
   164 {
   165     DISPMANX_MODEINFO_T modeinfo;
   166     DISPMANX_DISPLAY_HANDLE_T handle;
   167     SDL_VideoDisplay display;
   168     SDL_DisplayMode current_mode;
   169     SDL_DisplayData *data;
   170 
   171     handle = vc_dispmanx_display_open(display_id);
   172     if (!handle) {
   173         return;  /* this display isn't available */
   174     }
   175 
   176     if (vc_dispmanx_display_get_info(handle, &modeinfo) < 0) {
   177         vc_dispmanx_display_close(handle);
   178         return;
   179     }
   180 
   181     /* RPI_GetRefreshRate() doesn't distinguish between displays. I'm not sure the hardware distinguishes either */
   182     SDL_zero(current_mode);
   183     current_mode.w = modeinfo.width;
   184     current_mode.h = modeinfo.height;
   185     current_mode.refresh_rate = RPI_GetRefreshRate();
   186     /* 32 bpp for default */
   187     current_mode.format = SDL_PIXELFORMAT_ABGR8888;
   188 
   189     current_mode.driverdata = NULL;
   190 
   191     SDL_zero(display);
   192     display.desktop_mode = current_mode;
   193     display.current_mode = current_mode;
   194 
   195     /* Allocate display internal data */
   196     data = (SDL_DisplayData *) SDL_calloc(1, sizeof(SDL_DisplayData));
   197     if (data == NULL) {
   198         vc_dispmanx_display_close(handle);
   199         return;  /* oh well */
   200     }
   201 
   202     data->dispman_display = handle;
   203 
   204     display.driverdata = data;
   205 
   206     SDL_AddVideoDisplay(&display);
   207 }
   208 
   209 int
   210 RPI_VideoInit(_THIS)
   211 {
   212     /* Initialize BCM Host */
   213     bcm_host_init();
   214 
   215     AddDispManXDisplay(DISPMANX_ID_MAIN_LCD);  /* your default display */
   216     AddDispManXDisplay(DISPMANX_ID_FORCE_OTHER);  /* an "other" display...maybe DSI-connected screen while HDMI is your main */
   217 
   218 #ifdef SDL_INPUT_LINUXEV    
   219     if (SDL_EVDEV_Init() < 0) {
   220         return -1;
   221     }
   222 #endif    
   223     
   224     RPI_InitMouse(_this);
   225 
   226     return 1;
   227 }
   228 
   229 void
   230 RPI_VideoQuit(_THIS)
   231 {
   232 #ifdef SDL_INPUT_LINUXEV    
   233     SDL_EVDEV_Quit();
   234 #endif    
   235 }
   236 
   237 void
   238 RPI_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
   239 {
   240     /* Only one display mode available, the current one */
   241     SDL_AddDisplayMode(display, &display->current_mode);
   242 }
   243 
   244 int
   245 RPI_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
   246 {
   247     return 0;
   248 }
   249 
   250 static void
   251 RPI_vsync_callback(DISPMANX_UPDATE_HANDLE_T u, void *data)
   252 {
   253    SDL_WindowData *wdata = ((SDL_WindowData *) data);
   254 
   255    SDL_LockMutex(wdata->vsync_cond_mutex);
   256    SDL_CondSignal(wdata->vsync_cond);
   257    SDL_UnlockMutex(wdata->vsync_cond_mutex);
   258 }
   259 
   260 int
   261 RPI_CreateWindow(_THIS, SDL_Window * window)
   262 {
   263     SDL_WindowData *wdata;
   264     SDL_VideoDisplay *display;
   265     SDL_DisplayData *displaydata;
   266     VC_RECT_T dst_rect;
   267     VC_RECT_T src_rect;
   268     VC_DISPMANX_ALPHA_T         dispman_alpha;
   269     DISPMANX_UPDATE_HANDLE_T dispman_update;
   270     uint32_t layer = SDL_RPI_VIDEOLAYER;
   271     const char *env;
   272 
   273     /* Disable alpha, otherwise the app looks composed with whatever dispman is showing (X11, console,etc) */
   274     dispman_alpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS; 
   275     dispman_alpha.opacity = 0xFF; 
   276     dispman_alpha.mask = 0;
   277 
   278     /* Allocate window internal data */
   279     wdata = (SDL_WindowData *) SDL_calloc(1, sizeof(SDL_WindowData));
   280     if (wdata == NULL) {
   281         return SDL_OutOfMemory();
   282     }
   283     display = SDL_GetDisplayForWindow(window);
   284     displaydata = (SDL_DisplayData *) display->driverdata;
   285 
   286     /* Windows have one size for now */
   287     window->w = display->desktop_mode.w;
   288     window->h = display->desktop_mode.h;
   289 
   290     /* OpenGL ES is the law here, buddy */
   291     window->flags |= SDL_WINDOW_OPENGL;
   292 
   293     /* Create a dispman element and associate a window to it */
   294     dst_rect.x = 0;
   295     dst_rect.y = 0;
   296     dst_rect.width = window->w;
   297     dst_rect.height = window->h;
   298 
   299     src_rect.x = 0;
   300     src_rect.y = 0;
   301     src_rect.width = window->w << 16;
   302     src_rect.height = window->h << 16;
   303 
   304     env = SDL_GetHint(SDL_HINT_RPI_VIDEO_LAYER);
   305     if (env) {
   306         layer = SDL_atoi(env);
   307     }
   308 
   309     dispman_update = vc_dispmanx_update_start( 0 );
   310     wdata->dispman_window.element = vc_dispmanx_element_add (dispman_update,
   311                                                              displaydata->dispman_display,
   312                                                              layer /* layer */,
   313                                                              &dst_rect,
   314                                                              0 /*src*/,
   315                                                              &src_rect,
   316                                                              DISPMANX_PROTECTION_NONE,
   317                                                              &dispman_alpha /*alpha*/,
   318                                                              0 /*clamp*/,
   319                                                              0 /*transform*/);
   320     wdata->dispman_window.width = window->w;
   321     wdata->dispman_window.height = window->h;
   322     vc_dispmanx_update_submit_sync(dispman_update);
   323     
   324     if (!_this->egl_data) {
   325         if (SDL_GL_LoadLibrary(NULL) < 0) {
   326             return -1;
   327         }
   328     }
   329     wdata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) &wdata->dispman_window);
   330 
   331     if (wdata->egl_surface == EGL_NO_SURFACE) {
   332         return SDL_SetError("Could not create GLES window surface");
   333     }
   334 
   335     /* Start generating vsync callbacks if necesary */
   336     wdata->double_buffer = SDL_FALSE;
   337     if (SDL_GetHintBoolean(SDL_HINT_VIDEO_DOUBLE_BUFFER, SDL_FALSE)) {
   338         wdata->vsync_cond = SDL_CreateCond();
   339         wdata->vsync_cond_mutex = SDL_CreateMutex();
   340         wdata->double_buffer = SDL_TRUE;
   341         vc_dispmanx_vsync_callback(displaydata->dispman_display, RPI_vsync_callback, (void*)wdata);
   342     }
   343 
   344     /* Setup driver data for this window */
   345     window->driverdata = wdata;
   346 
   347     /* One window, it always has focus */
   348     SDL_SetMouseFocus(window);
   349     SDL_SetKeyboardFocus(window);
   350 
   351     /* Window has been successfully created */
   352     return 0;
   353 }
   354 
   355 void
   356 RPI_DestroyWindow(_THIS, SDL_Window * window)
   357 {
   358     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   359     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
   360     SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
   361 
   362     if(data) {
   363         if (data->double_buffer) {
   364             /* Wait for vsync, and then stop vsync callbacks and destroy related stuff, if needed */
   365             SDL_LockMutex(data->vsync_cond_mutex);
   366             SDL_CondWait(data->vsync_cond, data->vsync_cond_mutex);
   367             SDL_UnlockMutex(data->vsync_cond_mutex);
   368 
   369             vc_dispmanx_vsync_callback(displaydata->dispman_display, NULL, NULL);
   370 
   371             SDL_DestroyCond(data->vsync_cond);
   372             SDL_DestroyMutex(data->vsync_cond_mutex);
   373         }
   374 
   375 #if SDL_VIDEO_OPENGL_EGL
   376         if (data->egl_surface != EGL_NO_SURFACE) {
   377             SDL_EGL_DestroySurface(_this, data->egl_surface);
   378         }
   379 #endif
   380         SDL_free(data);
   381         window->driverdata = NULL;
   382     }
   383 }
   384 
   385 int
   386 RPI_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
   387 {
   388     return -1;
   389 }
   390 
   391 void
   392 RPI_SetWindowTitle(_THIS, SDL_Window * window)
   393 {
   394 }
   395 void
   396 RPI_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
   397 {
   398 }
   399 void
   400 RPI_SetWindowPosition(_THIS, SDL_Window * window)
   401 {
   402 }
   403 void
   404 RPI_SetWindowSize(_THIS, SDL_Window * window)
   405 {
   406 }
   407 void
   408 RPI_ShowWindow(_THIS, SDL_Window * window)
   409 {
   410 }
   411 void
   412 RPI_HideWindow(_THIS, SDL_Window * window)
   413 {
   414 }
   415 void
   416 RPI_RaiseWindow(_THIS, SDL_Window * window)
   417 {
   418 }
   419 void
   420 RPI_MaximizeWindow(_THIS, SDL_Window * window)
   421 {
   422 }
   423 void
   424 RPI_MinimizeWindow(_THIS, SDL_Window * window)
   425 {
   426 }
   427 void
   428 RPI_RestoreWindow(_THIS, SDL_Window * window)
   429 {
   430 }
   431 void
   432 RPI_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
   433 {
   434 
   435 }
   436 
   437 /*****************************************************************************/
   438 /* SDL Window Manager function                                               */
   439 /*****************************************************************************/
   440 #if 0
   441 SDL_bool
   442 RPI_GetWindowWMInfo(_THIS, SDL_Window * window, struct SDL_SysWMinfo *info)
   443 {
   444     if (info->version.major <= SDL_MAJOR_VERSION) {
   445         return SDL_TRUE;
   446     } else {
   447         SDL_SetError("application not compiled with SDL %d.%d",
   448                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
   449         return SDL_FALSE;
   450     }
   451 
   452     /* Failed to get window manager information */
   453     return SDL_FALSE;
   454 }
   455 #endif
   456 
   457 #endif /* SDL_VIDEO_DRIVER_RPI */
   458 
   459 /* vi: set ts=4 sw=4 expandtab: */