SDL  2.0
SDL_rpivideo.c
Go to the documentation of this file.
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);
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 {
81  SDL_VideoData *phdata;
82 
83  /* Initialize SDL_VideoDevice structure */
85  if (device == NULL) {
87  return NULL;
88  }
89 
90  /* Initialize internal data */
91  phdata = (SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
92  if (phdata == NULL) {
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 
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;
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 */
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
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 
218 
219  return 1;
220 }
221 
222 void
224 {
225 #ifdef SDL_INPUT_LINUXEV
226  SDL_EVDEV_Quit();
227 #endif
228 }
229 
230 void
232 {
233  /* Only one display mode available, the current one */
234  SDL_AddDisplayMode(display, &display->current_mode);
235 }
236 
237 int
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 
249  SDL_CondSignal(wdata->vsync_cond);
251 }
252 
253 int
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;
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 
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;
331  wdata->vsync_cond = SDL_CreateCond();
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 */
343 
344  /* Window has been successfully created */
345  return 0;
346 }
347 
348 void
350 {
351  SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
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
380 {
381  return -1;
382 }
383 
384 void
386 {
387 }
388 void
390 {
391 }
392 void
394 {
395 }
396 void
398 {
399 }
400 void
402 {
403 }
404 void
406 {
407 }
408 void
410 {
411 }
412 void
414 {
415 }
416 void
418 {
419 }
420 void
422 {
423 }
424 void
426 {
427 
428 }
429 
430 /*****************************************************************************/
431 /* SDL Window Manager function */
432 /*****************************************************************************/
433 #if 0
434 SDL_bool
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",
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: */
#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:88
#define SDL_HINT_RPI_VIDEO_LAYER
Tell SDL which Dispmanx layer to use on a Raspberry PI.
Definition: SDL_hints.h:1190
#define SDL_HINT_VIDEO_DOUBLE_BUFFER
Tell the video driver that we only want a double buffer.
Definition: SDL_hints.h:1208
void SDL_SetKeyboardFocus(SDL_Window *window)
Definition: SDL_keyboard.c:634
void SDL_SetMouseFocus(SDL_Window *window)
Definition: SDL_mouse.c:208
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:263
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_RaiseWindow(_THIS, SDL_Window *window)
void * RPI_GLES_GetProcAddress(_THIS, const char *proc)
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:426
SDL_bool
Definition: SDL_stdinc.h:168
@ SDL_TRUE
Definition: SDL_stdinc.h:170
@ SDL_FALSE
Definition: SDL_stdinc.h:169
VideoBootStrap RPI_bootstrap
int SDL_AddVideoDisplay(const SDL_VideoDisplay *display, SDL_bool send_event)
Definition: SDL_video.c:607
SDL_bool SDL_AddDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mode)
Definition: SDL_video.c:792
SDL_VideoDisplay * SDL_GetDisplayForWindow(SDL_Window *window)
Definition: SDL_video.c:1130
#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:126
@ SDL_WINDOW_OPENGL
Definition: SDL_video.h:100
#define NULL
Definition: begin_code.h:163
#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:218
SDL_DisplayMode desktop_mode
Definition: SDL_sysvideo.h:132
SDL_DisplayMode current_mode
Definition: SDL_sysvideo.h:133
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:75
Uint8 major
Definition: SDL_version.h:53