SDL 2.0
SDL_rpivideo.c
Go to the documentation of this file.
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
53static int
54RPI_Available(void)
55{
56 return 1;
57}
58
59static void
60RPI_Destroy(SDL_VideoDevice * device)
61{
62 SDL_free(device->driverdata);
64}
65
66static int
67RPI_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
83static SDL_VideoDevice *
84RPI_Create()
85{
87 SDL_VideoData *phdata;
88
89 /* Initialize SDL_VideoDevice structure */
91 if (device == NULL) {
93 return NULL;
94 }
95
96 /* Initialize internal data */
97 phdata = (SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
98 if (phdata == NULL) {
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
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
162static void
163AddDispManXDisplay(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;
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 */
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
209int
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
225
226 return 1;
227}
228
229void
231{
232#ifdef SDL_INPUT_LINUXEV
233 SDL_EVDEV_Quit();
234#endif
235}
236
237void
239{
240 /* Only one display mode available, the current one */
241 SDL_AddDisplayMode(display, &display->current_mode);
242}
243
244int
246{
247 return 0;
248}
249
250static void
251RPI_vsync_callback(DISPMANX_UPDATE_HANDLE_T u, void *data)
252{
253 SDL_WindowData *wdata = ((SDL_WindowData *) data);
254
258}
259
260int
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;
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 }
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
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;
338 wdata->vsync_cond = SDL_CreateCond();
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 */
350
351 /* Window has been successfully created */
352 return 0;
353}
354
355void
357{
358 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
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
385int
387{
388 return -1;
389}
390
391void
393{
394}
395void
397{
398}
399void
401{
402}
403void
405{
406}
407void
409{
410}
411void
413{
414}
415void
417{
418}
419void
421{
422}
423void
425{
426}
427void
429{
430}
431void
433{
434
435}
436
437/*****************************************************************************/
438/* SDL Window Manager function */
439/*****************************************************************************/
440#if 0
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",
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: */
#define _THIS
unsigned int uint32_t
#define SDL_SetError
#define SDL_CreateCond
#define SDL_CondWait
#define SDL_LockMutex
#define SDL_GL_LoadLibrary
#define SDL_CondSignal
#define SDL_CreateMutex
#define SDL_free
#define SDL_DestroyCond
#define SDL_GetHintBoolean
#define SDL_DestroyMutex
#define SDL_atoi
#define SDL_calloc
#define SDL_GetHint
#define SDL_UnlockMutex
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
#define SDL_HINT_RPI_VIDEO_LAYER
Tell SDL which Dispmanx layer to use on a Raspberry PI.
Definition: SDL_hints.h:975
#define SDL_HINT_VIDEO_DOUBLE_BUFFER
Tell the video driver that we only want a double buffer.
Definition: SDL_hints.h:993
void SDL_SetKeyboardFocus(SDL_Window *window)
Definition: SDL_keyboard.c:630
void SDL_SetMouseFocus(SDL_Window *window)
Definition: SDL_mouse.c:211
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
GLenum mode
GLenum GLuint GLint GLint layer
@ SDL_PIXELFORMAT_ABGR8888
Definition: SDL_pixels.h:254
void RPI_PumpEvents(_THIS)
void RPI_InitMouse(_THIS)
void RPI_ShowWindow(_THIS, SDL_Window *window)
void RPI_DestroyWindow(_THIS, SDL_Window *window)
int RPI_VideoInit(_THIS)
int RPI_GLES_SwapWindow(_THIS, SDL_Window *window)
void RPI_GLES_DeleteContext(_THIS, SDL_GLContext context)
void RPI_MaximizeWindow(_THIS, SDL_Window *window)
void RPI_GLES_UnloadLibrary(_THIS)
void RPI_RestoreWindow(_THIS, SDL_Window *window)
void RPI_VideoQuit(_THIS)
int RPI_CreateWindowFrom(_THIS, SDL_Window *window, const void *data)
void RPI_SetWindowPosition(_THIS, SDL_Window *window)
void RPI_SetWindowIcon(_THIS, SDL_Window *window, SDL_Surface *icon)
SDL_GLContext RPI_GLES_CreateContext(_THIS, SDL_Window *window)
int RPI_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
void * RPI_GLES_GetProcAddress(_THIS, const char *proc)
void RPI_RaiseWindow(_THIS, SDL_Window *window)
int RPI_GLES_LoadLibrary(_THIS, const char *path)
void RPI_SetWindowSize(_THIS, SDL_Window *window)
void RPI_SetWindowGrab(_THIS, SDL_Window *window, SDL_bool grabbed)
void RPI_GetDisplayModes(_THIS, SDL_VideoDisplay *display)
int RPI_CreateWindow(_THIS, SDL_Window *window)
void RPI_MinimizeWindow(_THIS, SDL_Window *window)
SDL_bool RPI_GetWindowWMInfo(_THIS, SDL_Window *window, struct SDL_SysWMinfo *info)
int RPI_GLES_MakeCurrent(_THIS, SDL_Window *window, SDL_GLContext context)
void RPI_SetWindowTitle(_THIS, SDL_Window *window)
int RPI_GLES_GetSwapInterval(_THIS)
#define SDL_RPI_VIDEOLAYER
Definition: SDL_rpivideo.h:59
void RPI_HideWindow(_THIS, SDL_Window *window)
int RPI_GLES_SetSwapInterval(_THIS, int interval)
#define SDL_zero(x)
Definition: SDL_stdinc.h:416
SDL_bool
Definition: SDL_stdinc.h:162
@ SDL_TRUE
Definition: SDL_stdinc.h:164
@ SDL_FALSE
Definition: SDL_stdinc.h:163
SDL_VideoDisplay * SDL_GetDisplayForWindow(SDL_Window *window)
Definition: SDL_video.c:1089
VideoBootStrap RPI_bootstrap
int SDL_AddVideoDisplay(const SDL_VideoDisplay *display)
Definition: SDL_video.c:603
SDL_bool SDL_AddDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mode)
Definition: SDL_video.c:751
#define SDL_MINOR_VERSION
Definition: SDL_version.h:61
#define SDL_MAJOR_VERSION
Definition: SDL_version.h:60
static SDL_VideoDevice * _this
Definition: SDL_video.c:118
@ SDL_WINDOW_OPENGL
Definition: SDL_video.h:101
#define NULL
Definition: begin_code.h:167
#define EGL_NO_SURFACE
Definition: egl.h:100
EGLImageKHR EGLint EGLint * handle
Definition: eglext.h:937
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
EGLNativeDisplayType * display_id
Definition: eglext.h:1024
EGLNativeWindowType NativeWindowType
Definition: eglplatform.h:112
static SDL_AudioDeviceID device
Definition: loopwave.c:37
DISPMANX_DISPLAY_HANDLE_T dispman_display
Definition: SDL_rpivideo.h:41
The structure that defines a display mode.
Definition: SDL_video.h:54
void * driverdata
Definition: SDL_video.h:59
Uint32 format
Definition: SDL_video.h:55
A collection of pixels used in software blitting.
Definition: SDL_surface.h:71
SDL_version version
Definition: SDL_syswm.h:199
SDL_DisplayMode desktop_mode
Definition: SDL_sysvideo.h:131
SDL_DisplayMode current_mode
Definition: SDL_sysvideo.h:132
SDL_bool double_buffer
SDL_cond * vsync_cond
Definition: SDL_rpivideo.h:53
EGLSurface egl_surface
SDL_mutex * vsync_cond_mutex
Definition: SDL_rpivideo.h:54
EGL_DISPMANX_WINDOW_T dispman_window
Definition: SDL_rpivideo.h:47
The type used to identify a window.
Definition: SDL_sysvideo.h:74
Uint8 major
Definition: SDL_version.h:53