SDL_rpivideo.c (13375B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 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 void 54 RPI_Destroy(SDL_VideoDevice * device) 55 { 56 SDL_free(device->driverdata); 57 SDL_free(device); 58 } 59 60 static int 61 RPI_GetRefreshRate() 62 { 63 TV_DISPLAY_STATE_T tvstate; 64 if (vc_tv_get_display_state( &tvstate ) == 0) { 65 //The width/height parameters are in the same position in the union 66 //for HDMI and SDTV 67 HDMI_PROPERTY_PARAM_T property; 68 property.property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE; 69 vc_tv_hdmi_get_property(&property); 70 return property.param1 == HDMI_PIXEL_CLOCK_TYPE_NTSC ? 71 tvstate.display.hdmi.frame_rate * (1000.0f/1001.0f) : 72 tvstate.display.hdmi.frame_rate; 73 } 74 return 60; /* Failed to get display state, default to 60 */ 75 } 76 77 static SDL_VideoDevice * 78 RPI_Create() 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 == NULL) { 86 SDL_OutOfMemory(); 87 return NULL; 88 } 89 90 /* Initialize internal data */ 91 phdata = (SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData)); 92 if (phdata == NULL) { 93 SDL_OutOfMemory(); 94 SDL_free(device); 95 return NULL; 96 } 97 98 device->driverdata = phdata; 99 100 /* Setup amount of available displays */ 101 device->num_displays = 0; 102 103 /* Set device free function */ 104 device->free = RPI_Destroy; 105 106 /* Setup all functions which we can handle */ 107 device->VideoInit = RPI_VideoInit; 108 device->VideoQuit = RPI_VideoQuit; 109 device->GetDisplayModes = RPI_GetDisplayModes; 110 device->SetDisplayMode = RPI_SetDisplayMode; 111 device->CreateSDLWindow = RPI_CreateWindow; 112 device->CreateSDLWindowFrom = RPI_CreateWindowFrom; 113 device->SetWindowTitle = RPI_SetWindowTitle; 114 device->SetWindowIcon = RPI_SetWindowIcon; 115 device->SetWindowPosition = RPI_SetWindowPosition; 116 device->SetWindowSize = RPI_SetWindowSize; 117 device->ShowWindow = RPI_ShowWindow; 118 device->HideWindow = RPI_HideWindow; 119 device->RaiseWindow = RPI_RaiseWindow; 120 device->MaximizeWindow = RPI_MaximizeWindow; 121 device->MinimizeWindow = RPI_MinimizeWindow; 122 device->RestoreWindow = RPI_RestoreWindow; 123 device->SetWindowGrab = RPI_SetWindowGrab; 124 device->DestroyWindow = RPI_DestroyWindow; 125 #if 0 126 device->GetWindowWMInfo = RPI_GetWindowWMInfo; 127 #endif 128 device->GL_LoadLibrary = RPI_GLES_LoadLibrary; 129 device->GL_GetProcAddress = RPI_GLES_GetProcAddress; 130 device->GL_UnloadLibrary = RPI_GLES_UnloadLibrary; 131 device->GL_CreateContext = RPI_GLES_CreateContext; 132 device->GL_MakeCurrent = RPI_GLES_MakeCurrent; 133 device->GL_SetSwapInterval = RPI_GLES_SetSwapInterval; 134 device->GL_GetSwapInterval = RPI_GLES_GetSwapInterval; 135 device->GL_SwapWindow = RPI_GLES_SwapWindow; 136 device->GL_DeleteContext = RPI_GLES_DeleteContext; 137 device->GL_DefaultProfileConfig = RPI_GLES_DefaultProfileConfig; 138 139 device->PumpEvents = RPI_PumpEvents; 140 141 return device; 142 } 143 144 VideoBootStrap RPI_bootstrap = { 145 "RPI", 146 "RPI Video Driver", 147 RPI_Create 148 }; 149 150 151 /*****************************************************************************/ 152 /* SDL Video and Display initialization/handling functions */ 153 /*****************************************************************************/ 154 155 static void 156 AddDispManXDisplay(const int display_id) 157 { 158 DISPMANX_MODEINFO_T modeinfo; 159 DISPMANX_DISPLAY_HANDLE_T handle; 160 SDL_VideoDisplay display; 161 SDL_DisplayMode current_mode; 162 SDL_DisplayData *data; 163 164 handle = vc_dispmanx_display_open(display_id); 165 if (!handle) { 166 return; /* this display isn't available */ 167 } 168 169 if (vc_dispmanx_display_get_info(handle, &modeinfo) < 0) { 170 vc_dispmanx_display_close(handle); 171 return; 172 } 173 174 /* RPI_GetRefreshRate() doesn't distinguish between displays. I'm not sure the hardware distinguishes either */ 175 SDL_zero(current_mode); 176 current_mode.w = modeinfo.width; 177 current_mode.h = modeinfo.height; 178 current_mode.refresh_rate = RPI_GetRefreshRate(); 179 /* 32 bpp for default */ 180 current_mode.format = SDL_PIXELFORMAT_ABGR8888; 181 182 current_mode.driverdata = NULL; 183 184 SDL_zero(display); 185 display.desktop_mode = current_mode; 186 display.current_mode = current_mode; 187 188 /* Allocate display internal data */ 189 data = (SDL_DisplayData *) SDL_calloc(1, sizeof(SDL_DisplayData)); 190 if (data == NULL) { 191 vc_dispmanx_display_close(handle); 192 return; /* oh well */ 193 } 194 195 data->dispman_display = handle; 196 197 display.driverdata = data; 198 199 SDL_AddVideoDisplay(&display, SDL_FALSE); 200 } 201 202 int 203 RPI_VideoInit(_THIS) 204 { 205 /* Initialize BCM Host */ 206 bcm_host_init(); 207 208 AddDispManXDisplay(DISPMANX_ID_MAIN_LCD); /* your default display */ 209 AddDispManXDisplay(DISPMANX_ID_FORCE_OTHER); /* an "other" display...maybe DSI-connected screen while HDMI is your main */ 210 211 #ifdef SDL_INPUT_LINUXEV 212 if (SDL_EVDEV_Init() < 0) { 213 return -1; 214 } 215 #endif 216 217 RPI_InitMouse(_this); 218 219 return 1; 220 } 221 222 void 223 RPI_VideoQuit(_THIS) 224 { 225 #ifdef SDL_INPUT_LINUXEV 226 SDL_EVDEV_Quit(); 227 #endif 228 } 229 230 void 231 RPI_GetDisplayModes(_THIS, SDL_VideoDisplay * display) 232 { 233 /* Only one display mode available, the current one */ 234 SDL_AddDisplayMode(display, &display->current_mode); 235 } 236 237 int 238 RPI_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode) 239 { 240 return 0; 241 } 242 243 static void 244 RPI_vsync_callback(DISPMANX_UPDATE_HANDLE_T u, void *data) 245 { 246 SDL_WindowData *wdata = ((SDL_WindowData *) data); 247 248 SDL_LockMutex(wdata->vsync_cond_mutex); 249 SDL_CondSignal(wdata->vsync_cond); 250 SDL_UnlockMutex(wdata->vsync_cond_mutex); 251 } 252 253 int 254 RPI_CreateWindow(_THIS, SDL_Window * window) 255 { 256 SDL_WindowData *wdata; 257 SDL_VideoDisplay *display; 258 SDL_DisplayData *displaydata; 259 VC_RECT_T dst_rect; 260 VC_RECT_T src_rect; 261 VC_DISPMANX_ALPHA_T dispman_alpha; 262 DISPMANX_UPDATE_HANDLE_T dispman_update; 263 uint32_t layer = SDL_RPI_VIDEOLAYER; 264 const char *env; 265 266 /* Disable alpha, otherwise the app looks composed with whatever dispman is showing (X11, console,etc) */ 267 dispman_alpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS; 268 dispman_alpha.opacity = 0xFF; 269 dispman_alpha.mask = 0; 270 271 /* Allocate window internal data */ 272 wdata = (SDL_WindowData *) SDL_calloc(1, sizeof(SDL_WindowData)); 273 if (wdata == NULL) { 274 return SDL_OutOfMemory(); 275 } 276 display = SDL_GetDisplayForWindow(window); 277 displaydata = (SDL_DisplayData *) display->driverdata; 278 279 /* Windows have one size for now */ 280 window->w = display->desktop_mode.w; 281 window->h = display->desktop_mode.h; 282 283 /* OpenGL ES is the law here, buddy */ 284 window->flags |= SDL_WINDOW_OPENGL; 285 286 /* Create a dispman element and associate a window to it */ 287 dst_rect.x = 0; 288 dst_rect.y = 0; 289 dst_rect.width = window->w; 290 dst_rect.height = window->h; 291 292 src_rect.x = 0; 293 src_rect.y = 0; 294 src_rect.width = window->w << 16; 295 src_rect.height = window->h << 16; 296 297 env = SDL_GetHint(SDL_HINT_RPI_VIDEO_LAYER); 298 if (env) { 299 layer = SDL_atoi(env); 300 } 301 302 dispman_update = vc_dispmanx_update_start( 0 ); 303 wdata->dispman_window.element = vc_dispmanx_element_add (dispman_update, 304 displaydata->dispman_display, 305 layer /* layer */, 306 &dst_rect, 307 0 /*src*/, 308 &src_rect, 309 DISPMANX_PROTECTION_NONE, 310 &dispman_alpha /*alpha*/, 311 0 /*clamp*/, 312 0 /*transform*/); 313 wdata->dispman_window.width = window->w; 314 wdata->dispman_window.height = window->h; 315 vc_dispmanx_update_submit_sync(dispman_update); 316 317 if (!_this->egl_data) { 318 if (SDL_GL_LoadLibrary(NULL) < 0) { 319 return -1; 320 } 321 } 322 wdata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) &wdata->dispman_window); 323 324 if (wdata->egl_surface == EGL_NO_SURFACE) { 325 return SDL_SetError("Could not create GLES window surface"); 326 } 327 328 /* Start generating vsync callbacks if necesary */ 329 wdata->double_buffer = SDL_FALSE; 330 if (SDL_GetHintBoolean(SDL_HINT_VIDEO_DOUBLE_BUFFER, SDL_FALSE)) { 331 wdata->vsync_cond = SDL_CreateCond(); 332 wdata->vsync_cond_mutex = SDL_CreateMutex(); 333 wdata->double_buffer = SDL_TRUE; 334 vc_dispmanx_vsync_callback(displaydata->dispman_display, RPI_vsync_callback, (void*)wdata); 335 } 336 337 /* Setup driver data for this window */ 338 window->driverdata = wdata; 339 340 /* One window, it always has focus */ 341 SDL_SetMouseFocus(window); 342 SDL_SetKeyboardFocus(window); 343 344 /* Window has been successfully created */ 345 return 0; 346 } 347 348 void 349 RPI_DestroyWindow(_THIS, SDL_Window * window) 350 { 351 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 352 SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); 353 SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; 354 355 if(data) { 356 if (data->double_buffer) { 357 /* Wait for vsync, and then stop vsync callbacks and destroy related stuff, if needed */ 358 SDL_LockMutex(data->vsync_cond_mutex); 359 SDL_CondWait(data->vsync_cond, data->vsync_cond_mutex); 360 SDL_UnlockMutex(data->vsync_cond_mutex); 361 362 vc_dispmanx_vsync_callback(displaydata->dispman_display, NULL, NULL); 363 364 SDL_DestroyCond(data->vsync_cond); 365 SDL_DestroyMutex(data->vsync_cond_mutex); 366 } 367 368 #if SDL_VIDEO_OPENGL_EGL 369 if (data->egl_surface != EGL_NO_SURFACE) { 370 SDL_EGL_DestroySurface(_this, data->egl_surface); 371 } 372 #endif 373 SDL_free(data); 374 window->driverdata = NULL; 375 } 376 } 377 378 int 379 RPI_CreateWindowFrom(_THIS, SDL_Window * window, const void *data) 380 { 381 return -1; 382 } 383 384 void 385 RPI_SetWindowTitle(_THIS, SDL_Window * window) 386 { 387 } 388 void 389 RPI_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon) 390 { 391 } 392 void 393 RPI_SetWindowPosition(_THIS, SDL_Window * window) 394 { 395 } 396 void 397 RPI_SetWindowSize(_THIS, SDL_Window * window) 398 { 399 } 400 void 401 RPI_ShowWindow(_THIS, SDL_Window * window) 402 { 403 } 404 void 405 RPI_HideWindow(_THIS, SDL_Window * window) 406 { 407 } 408 void 409 RPI_RaiseWindow(_THIS, SDL_Window * window) 410 { 411 } 412 void 413 RPI_MaximizeWindow(_THIS, SDL_Window * window) 414 { 415 } 416 void 417 RPI_MinimizeWindow(_THIS, SDL_Window * window) 418 { 419 } 420 void 421 RPI_RestoreWindow(_THIS, SDL_Window * window) 422 { 423 } 424 void 425 RPI_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed) 426 { 427 428 } 429 430 /*****************************************************************************/ 431 /* SDL Window Manager function */ 432 /*****************************************************************************/ 433 #if 0 434 SDL_bool 435 RPI_GetWindowWMInfo(_THIS, SDL_Window * window, struct SDL_SysWMinfo *info) 436 { 437 if (info->version.major <= SDL_MAJOR_VERSION) { 438 return SDL_TRUE; 439 } else { 440 SDL_SetError("application not compiled with SDL %d.%d", 441 SDL_MAJOR_VERSION, SDL_MINOR_VERSION); 442 return SDL_FALSE; 443 } 444 445 /* Failed to get window manager information */ 446 return SDL_FALSE; 447 } 448 #endif 449 450 #endif /* SDL_VIDEO_DRIVER_RPI */ 451 452 /* vi: set ts=4 sw=4 expandtab: */