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 #include "../../SDL_internal.h"
22 
23 #if SDL_VIDEO_DRIVER_DIRECTFB
24 
25 #include "SDL_DirectFB_video.h"
26 #include "SDL_DirectFB_modes.h"
27 
28 #define DFB_MAX_MODES 200
29 
30 struct screen_callback_t
31 {
32     int numscreens;
33     DFBScreenID screenid[DFB_MAX_SCREENS];
34     DFBDisplayLayerID gralayer[DFB_MAX_SCREENS];
35     DFBDisplayLayerID vidlayer[DFB_MAX_SCREENS];
36     int aux;                    /* auxiliary integer for callbacks */
37 };
38 
39 struct modes_callback_t
40 {
41     int nummodes;
42     SDL_DisplayMode *modelist;
43 };
44 
45 static DFBEnumerationResult
EnumModesCallback(int width,int height,int bpp,void * data)46 EnumModesCallback(int width, int height, int bpp, void *data)
47 {
48     struct modes_callback_t *modedata = (struct modes_callback_t *) data;
49     SDL_DisplayMode mode;
50 
51     mode.w = width;
52     mode.h = height;
53     mode.refresh_rate = 0;
54     mode.driverdata = NULL;
55     mode.format = SDL_PIXELFORMAT_UNKNOWN;
56 
57     if (modedata->nummodes < DFB_MAX_MODES) {
58         modedata->modelist[modedata->nummodes++] = mode;
59     }
60 
61     return DFENUM_OK;
62 }
63 
64 static DFBEnumerationResult
EnumScreensCallback(DFBScreenID screen_id,DFBScreenDescription desc,void * callbackdata)65 EnumScreensCallback(DFBScreenID screen_id, DFBScreenDescription desc,
66           void *callbackdata)
67 {
68     struct screen_callback_t *devdata = (struct screen_callback_t *) callbackdata;
69 
70     devdata->screenid[devdata->numscreens++] = screen_id;
71     return DFENUM_OK;
72 }
73 
74 static DFBEnumerationResult
EnumLayersCallback(DFBDisplayLayerID layer_id,DFBDisplayLayerDescription desc,void * callbackdata)75 EnumLayersCallback(DFBDisplayLayerID layer_id, DFBDisplayLayerDescription desc,
76          void *callbackdata)
77 {
78     struct screen_callback_t *devdata = (struct screen_callback_t *) callbackdata;
79 
80     if (desc.caps & DLCAPS_SURFACE) {
81         if ((desc.type & DLTF_GRAPHICS) && (desc.type & DLTF_VIDEO)) {
82             if (devdata->vidlayer[devdata->aux] == -1)
83                 devdata->vidlayer[devdata->aux] = layer_id;
84         } else if (desc.type & DLTF_GRAPHICS) {
85             if (devdata->gralayer[devdata->aux] == -1)
86                 devdata->gralayer[devdata->aux] = layer_id;
87         }
88     }
89     return DFENUM_OK;
90 }
91 
92 static void
CheckSetDisplayMode(_THIS,SDL_VideoDisplay * display,DFB_DisplayData * data,SDL_DisplayMode * mode)93 CheckSetDisplayMode(_THIS, SDL_VideoDisplay * display, DFB_DisplayData * data, SDL_DisplayMode * mode)
94 {
95     SDL_DFB_DEVICEDATA(_this);
96     DFBDisplayLayerConfig config;
97     DFBDisplayLayerConfigFlags failed;
98 
99     SDL_DFB_CHECKERR(data->layer->SetCooperativeLevel(data->layer,
100                                                       DLSCL_ADMINISTRATIVE));
101     config.width = mode->w;
102     config.height = mode->h;
103     config.pixelformat = DirectFB_SDLToDFBPixelFormat(mode->format);
104     config.flags = DLCONF_WIDTH | DLCONF_HEIGHT | DLCONF_PIXELFORMAT;
105     if (devdata->use_yuv_underlays) {
106         config.flags |= DLCONF_OPTIONS;
107         config.options = DLOP_ALPHACHANNEL;
108     }
109     failed = 0;
110     data->layer->TestConfiguration(data->layer, &config, &failed);
111     SDL_DFB_CHECKERR(data->layer->SetCooperativeLevel(data->layer,
112                                                       DLSCL_SHARED));
113     if (failed == 0)
114     {
115         SDL_AddDisplayMode(display, mode);
116         SDL_DFB_LOG("Mode %d x %d Added\n", mode->w, mode->h);
117     }
118     else
119         SDL_DFB_ERR("Mode %d x %d not available: %x\n", mode->w,
120                       mode->h, failed);
121 
122     return;
123   error:
124     return;
125 }
126 
127 
128 void
DirectFB_SetContext(_THIS,SDL_Window * window)129 DirectFB_SetContext(_THIS, SDL_Window *window)
130 {
131 #if (DFB_VERSION_ATLEAST(1,0,0))
132     /* FIXME: does not work on 1.0/1.2 with radeon driver
133      *        the approach did work with the matrox driver
134      *        This has simply no effect.
135      */
136 
137     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
138     DFB_DisplayData *dispdata = (DFB_DisplayData *) display->driverdata;
139 
140     /* FIXME: should we handle the error */
141     if (dispdata->vidIDinuse)
142         SDL_DFB_CHECK(dispdata->vidlayer->SwitchContext(dispdata->vidlayer,
143                                                            DFB_TRUE));
144 #endif
145 }
146 
147 void
DirectFB_InitModes(_THIS)148 DirectFB_InitModes(_THIS)
149 {
150     SDL_DFB_DEVICEDATA(_this);
151     IDirectFBDisplayLayer *layer = NULL;
152     SDL_VideoDisplay display;
153     DFB_DisplayData *dispdata = NULL;
154     SDL_DisplayMode mode;
155     DFBGraphicsDeviceDescription caps;
156     DFBDisplayLayerConfig dlc;
157     struct screen_callback_t *screencbdata;
158 
159     int tcw[DFB_MAX_SCREENS];
160     int tch[DFB_MAX_SCREENS];
161     int i;
162     DFBResult ret;
163 
164     SDL_DFB_ALLOC_CLEAR(screencbdata, sizeof(*screencbdata));
165 
166     screencbdata->numscreens = 0;
167 
168     for (i = 0; i < DFB_MAX_SCREENS; i++) {
169         screencbdata->gralayer[i] = -1;
170         screencbdata->vidlayer[i] = -1;
171     }
172 
173     SDL_DFB_CHECKERR(devdata->dfb->EnumScreens(devdata->dfb, &EnumScreensCallback,
174                                                screencbdata));
175 
176     for (i = 0; i < screencbdata->numscreens; i++) {
177         IDirectFBScreen *screen;
178 
179         SDL_DFB_CHECKERR(devdata->dfb->GetScreen(devdata->dfb,
180                                                  screencbdata->screenid
181                                                  [i], &screen));
182 
183         screencbdata->aux = i;
184         SDL_DFB_CHECKERR(screen->EnumDisplayLayers(screen, &EnumLayersCallback,
185                                                    screencbdata));
186         screen->GetSize(screen, &tcw[i], &tch[i]);
187 
188         screen->Release(screen);
189     }
190 
191     /* Query card capabilities */
192 
193     devdata->dfb->GetDeviceDescription(devdata->dfb, &caps);
194 
195     for (i = 0; i < screencbdata->numscreens; i++) {
196         SDL_DFB_CHECKERR(devdata->dfb->GetDisplayLayer(devdata->dfb,
197                                                        screencbdata->gralayer
198                                                        [i], &layer));
199 
200         SDL_DFB_CHECKERR(layer->SetCooperativeLevel(layer,
201                                                     DLSCL_ADMINISTRATIVE));
202         layer->EnableCursor(layer, 1);
203         SDL_DFB_CHECKERR(layer->SetCursorOpacity(layer, 0xC0));
204 
205         if (devdata->use_yuv_underlays) {
206             dlc.flags = DLCONF_PIXELFORMAT | DLCONF_OPTIONS;
207             dlc.pixelformat = DSPF_ARGB;
208             dlc.options = DLOP_ALPHACHANNEL;
209 
210             ret = layer->SetConfiguration(layer, &dlc);
211             if (ret != DFB_OK) {
212                 /* try AiRGB if the previous failed */
213                 dlc.pixelformat = DSPF_AiRGB;
214                 SDL_DFB_CHECKERR(layer->SetConfiguration(layer, &dlc));
215             }
216         }
217 
218         /* Query layer configuration to determine the current mode and pixelformat */
219         dlc.flags = DLCONF_ALL;
220         SDL_DFB_CHECKERR(layer->GetConfiguration(layer, &dlc));
221 
222         mode.format = DirectFB_DFBToSDLPixelFormat(dlc.pixelformat);
223 
224         if (mode.format == SDL_PIXELFORMAT_UNKNOWN) {
225             SDL_DFB_ERR("Unknown dfb pixelformat %x !\n", dlc.pixelformat);
226             goto error;
227         }
228 
229         mode.w = dlc.width;
230         mode.h = dlc.height;
231         mode.refresh_rate = 0;
232         mode.driverdata = NULL;
233 
234         SDL_DFB_ALLOC_CLEAR(dispdata, sizeof(*dispdata));
235 
236         dispdata->layer = layer;
237         dispdata->pixelformat = dlc.pixelformat;
238         dispdata->cw = tcw[i];
239         dispdata->ch = tch[i];
240 
241         /* YUV - Video layer */
242 
243         dispdata->vidID = screencbdata->vidlayer[i];
244         dispdata->vidIDinuse = 0;
245 
246         SDL_zero(display);
247 
248         display.desktop_mode = mode;
249         display.current_mode = mode;
250         display.driverdata = dispdata;
251 
252 #if (DFB_VERSION_ATLEAST(1,2,0))
253         dlc.flags =
254             DLCONF_WIDTH | DLCONF_HEIGHT | DLCONF_PIXELFORMAT |
255             DLCONF_OPTIONS;
256         ret = layer->SetConfiguration(layer, &dlc);
257 #endif
258 
259         SDL_DFB_CHECKERR(layer->SetCooperativeLevel(layer, DLSCL_SHARED));
260 
261         SDL_AddVideoDisplay(&display);
262     }
263     SDL_DFB_FREE(screencbdata);
264     return;
265   error:
266     /* FIXME: Cleanup not complete, Free existing displays */
267     SDL_DFB_FREE(dispdata);
268     SDL_DFB_RELEASE(layer);
269     return;
270 }
271 
272 void
DirectFB_GetDisplayModes(_THIS,SDL_VideoDisplay * display)273 DirectFB_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
274 {
275     SDL_DFB_DEVICEDATA(_this);
276     DFB_DisplayData *dispdata = (DFB_DisplayData *) display->driverdata;
277     SDL_DisplayMode mode;
278     struct modes_callback_t data;
279     int i;
280 
281     data.nummodes = 0;
282     /* Enumerate the available fullscreen modes */
283     SDL_DFB_CALLOC(data.modelist, DFB_MAX_MODES, sizeof(SDL_DisplayMode));
284     SDL_DFB_CHECKERR(devdata->dfb->EnumVideoModes(devdata->dfb,
285                                                   EnumModesCallback, &data));
286 
287     for (i = 0; i < data.nummodes; ++i) {
288         mode = data.modelist[i];
289 
290         mode.format = SDL_PIXELFORMAT_ARGB8888;
291         CheckSetDisplayMode(_this, display, dispdata, &mode);
292         mode.format = SDL_PIXELFORMAT_RGB888;
293         CheckSetDisplayMode(_this, display, dispdata, &mode);
294         mode.format = SDL_PIXELFORMAT_RGB24;
295         CheckSetDisplayMode(_this, display, dispdata, &mode);
296         mode.format = SDL_PIXELFORMAT_RGB565;
297         CheckSetDisplayMode(_this, display, dispdata, &mode);
298         mode.format = SDL_PIXELFORMAT_INDEX8;
299         CheckSetDisplayMode(_this, display, dispdata, &mode);
300     }
301 
302     SDL_DFB_FREE(data.modelist);
303 error:
304     return;
305 }
306 
307 int
DirectFB_SetDisplayMode(_THIS,SDL_VideoDisplay * display,SDL_DisplayMode * mode)308 DirectFB_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
309 {
310     /*
311      * FIXME: video mode switch is currently broken for 1.2.0
312      *
313      */
314 
315     SDL_DFB_DEVICEDATA(_this);
316     DFB_DisplayData *data = (DFB_DisplayData *) display->driverdata;
317     DFBDisplayLayerConfig config, rconfig;
318     DFBDisplayLayerConfigFlags fail = 0;
319 
320     SDL_DFB_CHECKERR(data->layer->SetCooperativeLevel(data->layer,
321                                                       DLSCL_ADMINISTRATIVE));
322 
323     SDL_DFB_CHECKERR(data->layer->GetConfiguration(data->layer, &config));
324     config.flags = DLCONF_WIDTH | DLCONF_HEIGHT;
325     if (mode->format != SDL_PIXELFORMAT_UNKNOWN) {
326         config.flags |= DLCONF_PIXELFORMAT;
327         config.pixelformat = DirectFB_SDLToDFBPixelFormat(mode->format);
328         data->pixelformat = config.pixelformat;
329     }
330     config.width = mode->w;
331     config.height = mode->h;
332 
333     if (devdata->use_yuv_underlays) {
334         config.flags |= DLCONF_OPTIONS;
335         config.options = DLOP_ALPHACHANNEL;
336     }
337 
338     data->layer->TestConfiguration(data->layer, &config, &fail);
339 
340     if (fail &
341         (DLCONF_WIDTH | DLCONF_HEIGHT | DLCONF_PIXELFORMAT |
342          DLCONF_OPTIONS)) {
343         SDL_DFB_ERR("Error setting mode %dx%d-%x\n", mode->w, mode->h,
344                     mode->format);
345         return -1;
346     }
347 
348     config.flags &= ~fail;
349     SDL_DFB_CHECKERR(data->layer->SetConfiguration(data->layer, &config));
350 #if (DFB_VERSION_ATLEAST(1,2,0))
351     /* Need to call this twice ! */
352     SDL_DFB_CHECKERR(data->layer->SetConfiguration(data->layer, &config));
353 #endif
354 
355     /* Double check */
356     SDL_DFB_CHECKERR(data->layer->GetConfiguration(data->layer, &rconfig));
357     SDL_DFB_CHECKERR(data->
358                      layer->SetCooperativeLevel(data->layer, DLSCL_SHARED));
359 
360     if ((config.width != rconfig.width) || (config.height != rconfig.height)
361         || ((mode->format != SDL_PIXELFORMAT_UNKNOWN)
362             && (config.pixelformat != rconfig.pixelformat))) {
363         SDL_DFB_ERR("Error setting mode %dx%d-%x\n", mode->w, mode->h,
364                     mode->format);
365         return -1;
366     }
367 
368     data->pixelformat = rconfig.pixelformat;
369     data->cw = config.width;
370     data->ch = config.height;
371     display->current_mode = *mode;
372 
373     return 0;
374   error:
375     return -1;
376 }
377 
378 void
DirectFB_QuitModes(_THIS)379 DirectFB_QuitModes(_THIS)
380 {
381     SDL_DisplayMode tmode;
382     int i;
383 
384     for (i = 0; i < _this->num_displays; ++i) {
385         SDL_VideoDisplay *display = &_this->displays[i];
386         DFB_DisplayData *dispdata = (DFB_DisplayData *) display->driverdata;
387 
388         SDL_GetDesktopDisplayMode(i, &tmode);
389         tmode.format = SDL_PIXELFORMAT_UNKNOWN;
390         DirectFB_SetDisplayMode(_this, display, &tmode);
391 
392         SDL_GetDesktopDisplayMode(i, &tmode);
393         DirectFB_SetDisplayMode(_this, display, &tmode);
394 
395         if (dispdata->layer) {
396             SDL_DFB_CHECK(dispdata->
397                           layer->SetCooperativeLevel(dispdata->layer,
398                                                      DLSCL_ADMINISTRATIVE));
399             SDL_DFB_CHECK(dispdata->
400                           layer->SetCursorOpacity(dispdata->layer, 0x00));
401             SDL_DFB_CHECK(dispdata->
402                           layer->SetCooperativeLevel(dispdata->layer,
403                                                      DLSCL_SHARED));
404         }
405 
406         SDL_DFB_RELEASE(dispdata->layer);
407         SDL_DFB_RELEASE(dispdata->vidlayer);
408 
409     }
410 }
411 
412 #endif /* SDL_VIDEO_DRIVER_DIRECTFB */
413 
414 /* vi: set ts=4 sw=4 expandtab: */
415