summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/video/raspberry/SDL_rpivideo.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/SDL-3.2.8/src/video/raspberry/SDL_rpivideo.c')
-rw-r--r--contrib/SDL-3.2.8/src/video/raspberry/SDL_rpivideo.c376
1 files changed, 376 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/video/raspberry/SDL_rpivideo.c b/contrib/SDL-3.2.8/src/video/raspberry/SDL_rpivideo.c
new file mode 100644
index 0000000..d313f9e
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/raspberry/SDL_rpivideo.c
@@ -0,0 +1,376 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 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#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