Atlas - SDL_rpivideo.c

Home / ext / SDL / src / video / raspberry Lines: 1 | Size: 11801 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 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#ifdef 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 "../../events/SDL_mouse_c.h" 36#include "../../events/SDL_keyboard_c.h" 37 38#ifdef SDL_INPUT_LINUXEV 39#include "../../core/linux/SDL_evdev.h" 40#endif 41 42// RPI declarations 43#include "SDL_rpivideo.h" 44#include "SDL_rpievents_c.h" 45#include "SDL_rpiopengles.h" 46#include "SDL_rpimouse.h" 47 48static void RPI_Destroy(SDL_VideoDevice *device) 49{ 50 SDL_free(device->internal); 51 SDL_free(device); 52} 53 54static void RPI_GetRefreshRate(int *numerator, int *denominator) 55{ 56 TV_DISPLAY_STATE_T tvstate; 57 if (vc_tv_get_display_state(&tvstate) == 0) { 58 // The width/height parameters are in the same position in the union 59 // for HDMI and SDTV 60 HDMI_PROPERTY_PARAM_T property; 61 property.property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE; 62 vc_tv_hdmi_get_property(&property); 63 if (property.param1 == HDMI_PIXEL_CLOCK_TYPE_NTSC) { 64 *numerator = tvstate.display.hdmi.frame_rate * 1000; 65 *denominator = 1001; 66 } else { 67 *numerator = tvstate.display.hdmi.frame_rate; 68 *denominator = 1; 69 } 70 return; 71 } 72 73 // Failed to get display state, default to 60 74 *numerator = 60; 75 *denominator = 1; 76} 77 78static SDL_VideoDevice *RPI_Create(void) 79{ 80 SDL_VideoDevice *device; 81 SDL_VideoData *phdata; 82 83 // Initialize SDL_VideoDevice structure 84 device = (SDL_VideoDevice *)SDL_calloc(1, sizeof(SDL_VideoDevice)); 85 if (!device) { 86 return NULL; 87 } 88 89 // Initialize internal data 90 phdata = (SDL_VideoData *)SDL_calloc(1, sizeof(SDL_VideoData)); 91 if (!phdata) { 92 SDL_free(device); 93 return NULL; 94 } 95 96 device->internal = phdata; 97 98 // Setup amount of available displays 99 device->num_displays = 0; 100 101 // Set device free function 102 device->free = RPI_Destroy; 103 104 // Setup all functions which we can handle 105 device->VideoInit = RPI_VideoInit; 106 device->VideoQuit = RPI_VideoQuit; 107 device->CreateSDLWindow = RPI_CreateWindow; 108 device->SetWindowTitle = RPI_SetWindowTitle; 109 device->SetWindowPosition = RPI_SetWindowPosition; 110 device->SetWindowSize = RPI_SetWindowSize; 111 device->ShowWindow = RPI_ShowWindow; 112 device->HideWindow = RPI_HideWindow; 113 device->RaiseWindow = RPI_RaiseWindow; 114 device->MaximizeWindow = RPI_MaximizeWindow; 115 device->MinimizeWindow = RPI_MinimizeWindow; 116 device->RestoreWindow = RPI_RestoreWindow; 117 device->DestroyWindow = RPI_DestroyWindow; 118 device->GL_LoadLibrary = RPI_GLES_LoadLibrary; 119 device->GL_GetProcAddress = RPI_GLES_GetProcAddress; 120 device->GL_UnloadLibrary = RPI_GLES_UnloadLibrary; 121 device->GL_CreateContext = RPI_GLES_CreateContext; 122 device->GL_MakeCurrent = RPI_GLES_MakeCurrent; 123 device->GL_SetSwapInterval = RPI_GLES_SetSwapInterval; 124 device->GL_GetSwapInterval = RPI_GLES_GetSwapInterval; 125 device->GL_SwapWindow = RPI_GLES_SwapWindow; 126 device->GL_DestroyContext = RPI_GLES_DestroyContext; 127 device->GL_DefaultProfileConfig = RPI_GLES_DefaultProfileConfig; 128 129 device->PumpEvents = RPI_PumpEvents; 130 131 return device; 132} 133 134VideoBootStrap RPI_bootstrap = { 135 "rpi", 136 "RPI Video Driver", 137 RPI_Create, 138 NULL, // no ShowMessageBox implementation 139 false 140}; 141 142/*****************************************************************************/ 143// SDL Video and Display initialization/handling functions 144/*****************************************************************************/ 145 146static void AddDispManXDisplay(const int display_id) 147{ 148 DISPMANX_MODEINFO_T modeinfo; 149 DISPMANX_DISPLAY_HANDLE_T handle; 150 SDL_VideoDisplay display; 151 SDL_DisplayMode mode; 152 SDL_DisplayData *data; 153 154 handle = vc_dispmanx_display_open(display_id); 155 if (!handle) { 156 return; // this display isn't available 157 } 158 159 if (vc_dispmanx_display_get_info(handle, &modeinfo) < 0) { 160 vc_dispmanx_display_close(handle); 161 return; 162 } 163 164 // RPI_GetRefreshRate() doesn't distinguish between displays. I'm not sure the hardware distinguishes either 165 SDL_zero(mode); 166 mode.w = modeinfo.width; 167 mode.h = modeinfo.height; 168 RPI_GetRefreshRate(&mode.refresh_rate_numerator, &mode.refresh_rate_denominator); 169 170 // 32 bpp for default 171 mode.format = SDL_PIXELFORMAT_ABGR8888; 172 173 SDL_zero(display); 174 display.desktop_mode = mode; 175 176 // Allocate display internal data 177 data = (SDL_DisplayData *)SDL_calloc(1, sizeof(SDL_DisplayData)); 178 if (!data) { 179 vc_dispmanx_display_close(handle); 180 return; // oh well 181 } 182 183 data->dispman_display = handle; 184 185 display.internal = data; 186 187 SDL_AddVideoDisplay(&display, false); 188} 189 190bool RPI_VideoInit(SDL_VideoDevice *_this) 191{ 192 // Initialize BCM Host 193 bcm_host_init(); 194 195 AddDispManXDisplay(DISPMANX_ID_MAIN_LCD); // your default display 196 AddDispManXDisplay(DISPMANX_ID_FORCE_OTHER); // an "other" display...maybe DSI-connected screen while HDMI is your main 197 198#ifdef SDL_INPUT_LINUXEV 199 if (!SDL_EVDEV_Init()) { 200 return false; 201 } 202#endif 203 204 RPI_InitMouse(_this); 205 206 return true; 207} 208 209void RPI_VideoQuit(SDL_VideoDevice *_this) 210{ 211#ifdef SDL_INPUT_LINUXEV 212 SDL_EVDEV_Quit(); 213#endif 214} 215 216static void RPI_vsync_callback(DISPMANX_UPDATE_HANDLE_T u, void *data) 217{ 218 SDL_WindowData *wdata = (SDL_WindowData *)data; 219 220 SDL_LockMutex(wdata->vsync_cond_mutex); 221 SDL_SignalCondition(wdata->vsync_cond); 222 SDL_UnlockMutex(wdata->vsync_cond_mutex); 223} 224 225bool RPI_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props) 226{ 227 SDL_WindowData *wdata; 228 SDL_VideoDisplay *display; 229 SDL_DisplayData *displaydata; 230 VC_RECT_T dst_rect; 231 VC_RECT_T src_rect; 232 VC_DISPMANX_ALPHA_T dispman_alpha; 233 DISPMANX_UPDATE_HANDLE_T dispman_update; 234 uint32_t layer = SDL_RPI_VIDEOLAYER; 235 const char *env; 236 237 // Disable alpha, otherwise the app looks composed with whatever dispman is showing (X11, console,etc) 238 dispman_alpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS; 239 dispman_alpha.opacity = 0xFF; 240 dispman_alpha.mask = 0; 241 242 // Allocate window internal data 243 wdata = (SDL_WindowData *)SDL_calloc(1, sizeof(SDL_WindowData)); 244 if (!wdata) { 245 return false; 246 } 247 display = SDL_GetVideoDisplayForWindow(window); 248 displaydata = display->internal; 249 250 // Windows have one size for now 251 window->w = display->desktop_mode.w; 252 window->h = display->desktop_mode.h; 253 254 // OpenGL ES is the law here, buddy 255 window->flags |= SDL_WINDOW_OPENGL; 256 257 // Create a dispman element and associate a window to it 258 dst_rect.x = 0; 259 dst_rect.y = 0; 260 dst_rect.width = window->w; 261 dst_rect.height = window->h; 262 263 src_rect.x = 0; 264 src_rect.y = 0; 265 src_rect.width = window->w << 16; 266 src_rect.height = window->h << 16; 267 268 env = SDL_GetHint(SDL_HINT_RPI_VIDEO_LAYER); 269 if (env) { 270 layer = SDL_atoi(env); 271 } 272 273 dispman_update = vc_dispmanx_update_start(0); 274 wdata->dispman_window.element = vc_dispmanx_element_add(dispman_update, 275 displaydata->dispman_display, 276 layer /* layer */, 277 &dst_rect, 278 0 /*src*/, 279 &src_rect, 280 DISPMANX_PROTECTION_NONE, 281 &dispman_alpha /*alpha*/, 282 0 /*clamp*/, 283 0 /*transform*/); 284 wdata->dispman_window.width = window->w; 285 wdata->dispman_window.height = window->h; 286 vc_dispmanx_update_submit_sync(dispman_update); 287 288 if (!_this->egl_data) { 289 if (!SDL_GL_LoadLibrary(NULL)) { 290 return false; 291 } 292 } 293 wdata->egl_surface = SDL_EGL_CreateSurface(_this, window, (NativeWindowType)&wdata->dispman_window); 294 295 if (wdata->egl_surface == EGL_NO_SURFACE) { 296 return SDL_SetError("Could not create GLES window surface"); 297 } 298 299 // Start generating vsync callbacks if necessary 300 wdata->double_buffer = false; 301 if (SDL_GetHintBoolean(SDL_HINT_VIDEO_DOUBLE_BUFFER, false)) { 302 wdata->vsync_cond = SDL_CreateCondition(); 303 wdata->vsync_cond_mutex = SDL_CreateMutex(); 304 wdata->double_buffer = true; 305 vc_dispmanx_vsync_callback(displaydata->dispman_display, RPI_vsync_callback, (void *)wdata); 306 } 307 308 // Setup driver data for this window 309 window->internal = wdata; 310 311 // One window, it always has focus 312 SDL_SetMouseFocus(window); 313 SDL_SetKeyboardFocus(window); 314 315 // Window has been successfully created 316 return true; 317} 318 319void RPI_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window) 320{ 321 SDL_WindowData *data = window->internal; 322 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); 323 324 if (data) { 325 if (data->double_buffer) { 326 // Wait for vsync, and then stop vsync callbacks and destroy related stuff, if needed 327 SDL_LockMutex(data->vsync_cond_mutex); 328 SDL_WaitCondition(data->vsync_cond, data->vsync_cond_mutex); 329 SDL_UnlockMutex(data->vsync_cond_mutex); 330 331 vc_dispmanx_vsync_callback(displaydata->dispman_display, NULL, NULL); 332 333 SDL_DestroyCondition(data->vsync_cond); 334 SDL_DestroyMutex(data->vsync_cond_mutex); 335 } 336 337#ifdef SDL_VIDEO_OPENGL_EGL 338 if (data->egl_surface != EGL_NO_SURFACE) { 339 SDL_EGL_DestroySurface(_this, data->egl_surface); 340 } 341#endif 342 SDL_free(data); 343 window->internal = NULL; 344 } 345} 346 347void RPI_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window) 348{ 349} 350bool RPI_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) 351{ 352 return SDL_Unsupported(); 353} 354void RPI_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) 355{ 356} 357void RPI_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) 358{ 359} 360void RPI_HideWindow(SDL_VideoDevice *_this, SDL_Window *window) 361{ 362} 363void RPI_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window) 364{ 365} 366void RPI_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window) 367{ 368} 369void RPI_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window) 370{ 371} 372void RPI_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window) 373{ 374} 375 376#endif // SDL_VIDEO_DRIVER_RPI 377
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.