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 
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 {
87  SDL_VideoData *phdata;
88 
89  /* Initialize SDL_VideoDevice structure */
90  device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
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) {
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;
123  device->ShowWindow = RPI_ShowWindow;
124  device->HideWindow = RPI_HideWindow;
125  device->RaiseWindow = RPI_RaiseWindow;
131 #if 0
133 #endif
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 
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;
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
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 
229 void
231 {
232 #ifdef SDL_INPUT_LINUXEV
233  SDL_EVDEV_Quit();
234 #endif
235 }
236 
237 void
239 {
240  /* Only one display mode available, the current one */
241  SDL_AddDisplayMode(display, &display->current_mode);
242 }
243 
244 int
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 
256  SDL_CondSignal(wdata->vsync_cond);
258 }
259 
260 int
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  }
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 
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 */
348  SDL_SetMouseFocus(window);
349  SDL_SetKeyboardFocus(window);
350 
351  /* Window has been successfully created */
352  return 0;
353 }
354 
355 void
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 */
368 
369  vc_dispmanx_vsync_callback(displaydata->dispman_display, NULL, NULL);
370 
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
393 {
394 }
395 void
397 {
398 }
399 void
401 {
402 }
403 void
405 {
406 }
407 void
409 {
410 }
411 void
413 {
414 }
415 void
417 {
418 }
419 void
421 {
422 }
423 void
425 {
426 }
427 void
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",
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: */
EGLNativeDisplayType * display_id
Definition: eglext.h:1024
#define SDL_RPI_VIDEOLAYER
Definition: SDL_rpivideo.h:59
SDL_cond * vsync_cond
Definition: SDL_rpivideo.h:53
#define SDL_MINOR_VERSION
Definition: SDL_version.h:61
SDL_bool double_buffer
#define SDL_LockMutex
void RPI_DestroyWindow(_THIS, SDL_Window *window)
void RPI_RestoreWindow(_THIS, SDL_Window *window)
void(* RestoreWindow)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:228
void * RPI_GLES_GetProcAddress(_THIS, const char *proc)
void SDL_SetKeyboardFocus(SDL_Window *window)
Definition: SDL_keyboard.c:630
int RPI_CreateWindowFrom(_THIS, SDL_Window *window, const void *data)
void RPI_SetWindowTitle(_THIS, SDL_Window *window)
#define EGL_NO_SURFACE
Definition: egl.h:100
void RPI_SetWindowGrab(_THIS, SDL_Window *window, SDL_bool grabbed)
int RPI_GLES_LoadLibrary(_THIS, const char *path)
#define SDL_MAJOR_VERSION
Definition: SDL_version.h:60
void(* free)(_THIS)
Definition: SDL_sysvideo.h:394
#define SDL_CreateMutex
void RPI_GLES_UnloadLibrary(_THIS)
void RPI_GetDisplayModes(_THIS, SDL_VideoDisplay *display)
A collection of pixels used in software blitting.
Definition: SDL_surface.h:70
int(* GL_SetSwapInterval)(_THIS, int interval)
Definition: SDL_sysvideo.h:261
SDL_mutex * vsync_cond_mutex
Definition: SDL_rpivideo.h:54
int RPI_GLES_MakeCurrent(_THIS, SDL_Window *window, SDL_GLContext context)
void(* ShowWindow)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:223
The structure that defines a display mode.
Definition: SDL_video.h:53
#define SDL_GetHint
SDL_version version
Definition: SDL_syswm.h:199
Uint8 major
Definition: SDL_version.h:53
void(* SetWindowSize)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:216
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
void RPI_RaiseWindow(_THIS, SDL_Window *window)
GLenum GLuint GLint GLint layer
int RPI_GLES_GetSwapInterval(_THIS)
void RPI_MaximizeWindow(_THIS, SDL_Window *window)
#define SDL_DestroyCond
void SDL_SetMouseFocus(SDL_Window *window)
Definition: SDL_mouse.c:211
#define SDL_GL_LoadLibrary
#define SDL_CondSignal
#define SDL_CondWait
int SDL_AddVideoDisplay(const SDL_VideoDisplay *display)
Definition: SDL_video.c:603
int(* GL_LoadLibrary)(_THIS, const char *path)
Definition: SDL_sysvideo.h:255
int RPI_VideoInit(_THIS)
int(* SetDisplayMode)(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
Definition: SDL_sysvideo.h:205
#define SDL_GetHintBoolean
void RPI_SetWindowIcon(_THIS, SDL_Window *window, SDL_Surface *icon)
static SDL_VideoDevice * _this
Definition: SDL_video.c:118
EGLNativeWindowType NativeWindowType
Definition: eglplatform.h:112
void(* HideWindow)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:224
static SDL_AudioDeviceID device
Definition: loopwave.c:37
void(* RaiseWindow)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:225
SDL_bool(* GetWindowWMInfo)(_THIS, SDL_Window *window, struct SDL_SysWMinfo *info)
Definition: SDL_sysvideo.h:248
SDL_GLContext(* GL_CreateContext)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:258
EGLImageKHR EGLint EGLint * handle
Definition: eglext.h:937
#define _THIS
void RPI_PumpEvents(_THIS)
int(* GL_MakeCurrent)(_THIS, SDL_Window *window, SDL_GLContext context)
Definition: SDL_sysvideo.h:259
#define SDL_free
EGL_DISPMANX_WINDOW_T dispman_window
Definition: SDL_rpivideo.h:47
void * driverdata
Definition: SDL_video.h:59
void RPI_HideWindow(_THIS, SDL_Window *window)
void RPI_SetWindowSize(_THIS, SDL_Window *window)
SDL_DisplayMode current_mode
Definition: SDL_sysvideo.h:132
GLenum mode
void(* DestroyWindow)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:235
void RPI_GLES_DeleteContext(_THIS, SDL_GLContext context)
#define SDL_zero(x)
Definition: SDL_stdinc.h:416
void(* SetWindowIcon)(_THIS, SDL_Window *window, SDL_Surface *icon)
Definition: SDL_sysvideo.h:214
#define SDL_CreateCond
DISPMANX_DISPLAY_HANDLE_T dispman_display
Definition: SDL_rpivideo.h:41
void RPI_VideoQuit(_THIS)
#define SDL_atoi
void(* GL_UnloadLibrary)(_THIS)
Definition: SDL_sysvideo.h:257
void(* GetDisplayModes)(_THIS, SDL_VideoDisplay *display)
Definition: SDL_sysvideo.h:197
VideoBootStrap RPI_bootstrap
#define NULL
Definition: begin_code.h:167
int(* CreateSDLWindow)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:211
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:161
SDL_DisplayMode desktop_mode
Definition: SDL_sysvideo.h:131
unsigned int uint32_t
void(* VideoQuit)(_THIS)
Definition: SDL_sysvideo.h:167
void RPI_ShowWindow(_THIS, SDL_Window *window)
void RPI_MinimizeWindow(_THIS, SDL_Window *window)
#define SDL_SetError
#define SDL_HINT_RPI_VIDEO_LAYER
Tell SDL which Dispmanx layer to use on a Raspberry PI.
Definition: SDL_hints.h:975
#define SDL_DestroyMutex
#define SDL_calloc
int RPI_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
SDL_VideoDisplay * SDL_GetDisplayForWindow(SDL_Window *window)
Definition: SDL_video.c:1089
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
void(* SetWindowPosition)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:215
int(* GL_SwapWindow)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:263
#define SDL_HINT_VIDEO_DOUBLE_BUFFER
Tell the video driver that we only want a double buffer.
Definition: SDL_hints.h:993
The type used to identify a window.
Definition: SDL_sysvideo.h:73
SDL_GLContext RPI_GLES_CreateContext(_THIS, SDL_Window *window)
void(* MinimizeWindow)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:227
int RPI_CreateWindow(_THIS, SDL_Window *window)
SDL_bool SDL_AddDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mode)
Definition: SDL_video.c:751
int(* VideoInit)(_THIS)
Definition: SDL_sysvideo.h:161
int(* CreateSDLWindowFrom)(_THIS, SDL_Window *window, const void *data)
Definition: SDL_sysvideo.h:212
#define SDL_UnlockMutex
void * driverdata
Definition: SDL_sysvideo.h:111
void(* GL_DeleteContext)(_THIS, SDL_GLContext context)
Definition: SDL_sysvideo.h:264
void(* GL_DefaultProfileConfig)(_THIS, int *mask, int *major, int *minor)
Definition: SDL_sysvideo.h:265
Uint32 format
Definition: SDL_video.h:55
void(* SetWindowTitle)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:213
int(* GL_GetSwapInterval)(_THIS)
Definition: SDL_sysvideo.h:262
void(* MaximizeWindow)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:226
Uint32 flags
Definition: SDL_sysvideo.h:83
SDL_bool RPI_GetWindowWMInfo(_THIS, SDL_Window *window, struct SDL_SysWMinfo *info)
void RPI_SetWindowPosition(_THIS, SDL_Window *window)
void(* SetWindowGrab)(_THIS, SDL_Window *window, SDL_bool grabbed)
Definition: SDL_sysvideo.h:234
void RPI_InitMouse(_THIS)
int RPI_GLES_SwapWindow(_THIS, SDL_Window *window)
void *(* GL_GetProcAddress)(_THIS, const char *proc)
Definition: SDL_sysvideo.h:256
EGLSurface egl_surface
void(* PumpEvents)(_THIS)
Definition: SDL_sysvideo.h:281
int RPI_GLES_SetSwapInterval(_THIS, int interval)