1/* Blackfin GUI (SDL) helper code
2
3   Copyright (C) 2010-2023 Free Software Foundation, Inc.
4   Contributed by Analog Devices, Inc.
5
6   This file is part of simulators.
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 3 of the License, or
11   (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
20
21/* This must come before any other includes.  */
22#include "defs.h"
23
24#ifdef HAVE_SDL
25# include <SDL.h>
26#endif
27#ifdef HAVE_DLFCN_H
28# include <dlfcn.h>
29#endif
30
31#include "libiberty.h"
32#include "gui.h"
33
34#ifdef HAVE_SDL
35
36static struct {
37  void *handle;
38  int (*Init) (Uint32 flags);
39  void (*Quit) (void);
40  int (*ShowCursor) (int toggle);
41  int (*LockSurface) (SDL_Surface *surface);
42  void (*UnlockSurface) (SDL_Surface *surface);
43#if HAVE_SDL == 1
44  void (*GetRGB) (Uint32 pixel, const SDL_PixelFormat * const fmt, Uint8 *r, Uint8 *g, Uint8 *b);
45  Uint32 (*MapRGB) (const SDL_PixelFormat * const format, const Uint8 r, const Uint8 g, const Uint8 b);
46  SDL_Surface *(*SetVideoMode) (int width, int height, int bpp, Uint32 flags);
47  void (*WM_SetCaption) (const char *title, const char *icon);
48  void (*UpdateRect) (SDL_Surface *screen, Sint32 x, Sint32 y, Uint32 w, Uint32 h);
49#else
50  void (*GetRGB) (Uint32 pixel, const SDL_PixelFormat *fmt, Uint8 *r, Uint8 *g, Uint8 *b);
51  Uint32 (*MapRGB) (const SDL_PixelFormat *format, Uint8 r, Uint8 g, Uint8 b);
52  SDL_Window *(*CreateWindow) (const char *title, int x, int y, int w, int h, Uint32 flags);
53  SDL_Surface *(*GetWindowSurface) (SDL_Window *window);
54  SDL_PixelFormat *(*AllocFormat) (Uint32 pixel_format);
55  int (*UpdateWindowSurfaceRects) (SDL_Window *window, const SDL_Rect *rects, int numrects);
56#endif
57} sdl;
58
59static const char * const sdl_syms[] =
60{
61  "SDL_Init",
62  "SDL_Quit",
63  "SDL_ShowCursor",
64  "SDL_LockSurface",
65  "SDL_UnlockSurface",
66  "SDL_GetRGB",
67  "SDL_MapRGB",
68#if HAVE_SDL == 1
69  "SDL_SetVideoMode",
70  "SDL_WM_SetCaption",
71  "SDL_UpdateRect",
72#else
73  "SDL_CreateWindow",
74  "SDL_GetWindowSurface",
75  "SDL_AllocFormat",
76  "SDL_UpdateWindowSurfaceRects",
77#endif
78};
79
80struct gui_state {
81#if HAVE_SDL == 2
82  SDL_Window *window;
83#endif
84  SDL_Surface *screen;
85  const SDL_PixelFormat *format;
86  int throttle, throttle_limit;
87  enum gui_color color;
88  int bytes_per_pixel;
89  int curr_line;
90};
91
92static const char bfin_gui_window_title[] = "Blackfin GNU Simulator";
93
94/* Load the SDL lib on the fly to avoid hard linking against it.  */
95static int
96bfin_gui_sdl_setup (void)
97{
98  static const char libsdl_soname[] =
99#if HAVE_SDL == 1
100      "libSDL-1.2.so.0";
101#else
102      "libSDL2-2.0.so.0";
103#endif
104  int i;
105  uintptr_t **funcs;
106
107  if (sdl.handle)
108    return 0;
109
110  sdl.handle = dlopen (libsdl_soname, RTLD_LAZY);
111  if (sdl.handle == NULL)
112    return -1;
113
114  funcs = (void *) &sdl.Init;
115  for (i = 0; i < ARRAY_SIZE (sdl_syms); ++i)
116    {
117      funcs[i] = dlsym (sdl.handle, sdl_syms[i]);
118      if (funcs[i] == NULL)
119	{
120	  dlclose (sdl.handle);
121	  sdl.handle = NULL;
122	  return -1;
123	}
124    }
125
126  return 0;
127}
128
129static const SDL_PixelFormat *bfin_gui_color_format (enum gui_color color,
130						     int *bytes_per_pixel);
131
132void *
133bfin_gui_setup (void *state, int enabled, int width, int height,
134		enum gui_color color)
135{
136  if (bfin_gui_sdl_setup ())
137    return NULL;
138
139  /* Create an SDL window if enabled and we don't have one yet.  */
140  if (enabled && !state)
141    {
142      struct gui_state *gui = xmalloc (sizeof (*gui));
143      if (!gui)
144	return NULL;
145
146      if (sdl.Init (SDL_INIT_VIDEO))
147	goto error;
148
149      gui->color = color;
150      gui->format = bfin_gui_color_format (gui->color, &gui->bytes_per_pixel);
151#if HAVE_SDL == 1
152      sdl.WM_SetCaption (bfin_gui_window_title, NULL);
153      gui->screen = sdl.SetVideoMode (width, height, 32,
154				      SDL_ANYFORMAT|SDL_HWSURFACE);
155#else
156      gui->window = sdl.CreateWindow (
157	  bfin_gui_window_title, SDL_WINDOWPOS_CENTERED,
158	  SDL_WINDOWPOS_CENTERED, width, height, 0);
159      if (!gui->window)
160	{
161	  sdl.Quit();
162	  goto error;
163	}
164
165      gui->screen = sdl.GetWindowSurface(gui->window);
166#endif
167      if (!gui->screen)
168	{
169	  sdl.Quit();
170	  goto error;
171	}
172
173      sdl.ShowCursor (0);
174      gui->curr_line = 0;
175      gui->throttle = 0;
176      gui->throttle_limit = 0xf; /* XXX: let people control this ?  */
177      return gui;
178
179 error:
180      free (gui);
181      return NULL;
182    }
183
184  /* Else break down a window if disabled and we had one.  */
185  else if (!enabled && state)
186    {
187      sdl.Quit();
188      free (state);
189      return NULL;
190    }
191
192  /* Retain existing state, whatever that may be.  */
193  return state;
194}
195
196static int
197SDL_ConvertBlitLineFrom (const Uint8 *src, const SDL_PixelFormat * const format,
198#if HAVE_SDL == 2
199			 SDL_Window *win,
200#endif
201			 SDL_Surface *dst, int dsty, int bytes_per_pixel)
202{
203  Uint8 r, g, b;
204  Uint32 *pixels;
205  unsigned i, j;
206
207  if (SDL_MUSTLOCK (dst))
208    if (sdl.LockSurface (dst))
209      return 1;
210
211  pixels = dst->pixels;
212  pixels += (dsty * dst->pitch / 4);
213
214  for (i = 0; i < dst->w; ++i)
215    {
216      /* Exract the packed source pixel; RGB or BGR.  */
217      Uint32 pix = 0;
218      for (j = 0; j < bytes_per_pixel; ++j)
219	if (format->Rshift)
220	  pix = (pix << 8) | src[j];
221	else
222	  pix = pix | ((Uint32)src[j] << (j * 8));
223
224      /* Unpack the source pixel into its components.  */
225      sdl.GetRGB (pix, format, &r, &g, &b);
226      /* Translate into the screen pixel format.  */
227      *pixels++ = sdl.MapRGB (dst->format, r, g, b);
228
229      src += bytes_per_pixel;
230    }
231
232  if (SDL_MUSTLOCK (dst))
233    sdl.UnlockSurface (dst);
234
235#if HAVE_SDL == 1
236  sdl.UpdateRect (dst, 0, dsty, dst->w, 1);
237#else
238  {
239    SDL_Rect rect = {
240      .x = 0,
241      .y = dsty,
242      .w = dst->w,
243      .h = 1,
244    };
245
246    sdl.UpdateWindowSurfaceRects (win, &rect, 1);
247  }
248#endif
249
250  return 0;
251}
252
253unsigned
254bfin_gui_update (void *state, const void *source, unsigned nr_bytes)
255{
256  struct gui_state *gui = state;
257  int ret;
258
259  if (!gui)
260    return 0;
261
262  /* XXX: Make this an option ?  */
263  gui->throttle = (gui->throttle + 1) & gui->throttle_limit;
264  if (gui->throttle)
265    return 0;
266
267  ret = SDL_ConvertBlitLineFrom (source, gui->format,
268#if HAVE_SDL == 2
269				 gui->window,
270#endif
271				 gui->screen, gui->curr_line,
272				 gui->bytes_per_pixel);
273  if (ret)
274    return 0;
275
276  gui->curr_line = (gui->curr_line + 1) % gui->screen->h;
277
278  return nr_bytes;
279}
280
281#if HAVE_SDL == 1
282
283#define FMASK(cnt, shift) (((1 << (cnt)) - 1) << (shift))
284#define _FORMAT(bpp, rcnt, gcnt, bcnt, acnt, rsh, gsh, bsh, ash) \
285  NULL, bpp, (bpp)/8, 8-(rcnt), 8-(gcnt), 8-(bcnt), 8-(acnt), rsh, gsh, bsh, ash, \
286  FMASK (rcnt, rsh), FMASK (gcnt, gsh), FMASK (bcnt, bsh), FMASK (acnt, ash),
287#define FORMAT(rcnt, gcnt, bcnt, acnt, rsh, gsh, bsh, ash) \
288  _FORMAT(((((rcnt) + (gcnt) + (bcnt) + (acnt)) + 7) / 8) * 8, \
289	  rcnt, gcnt, bcnt, acnt, rsh, gsh, bsh, ash)
290
291static const SDL_PixelFormat sdl_rgb_565 =
292{
293  FORMAT (5, 6, 5, 0, 11, 5, 0, 0)
294};
295#define SDL_PIXELFORMAT_RGB565 &sdl_rgb_565
296
297static const SDL_PixelFormat sdl_bgr_565 =
298{
299  FORMAT (5, 6, 5, 0, 0, 5, 11, 0)
300};
301#define SDL_PIXELFORMAT_BGR565 &sdl_bgr_565
302
303static const SDL_PixelFormat sdl_rgb_888 =
304{
305  FORMAT (8, 8, 8, 0, 16, 8, 0, 0)
306};
307#define SDL_PIXELFORMAT_RGB888 &sdl_rgb_888
308
309static const SDL_PixelFormat sdl_bgr_888 =
310{
311  FORMAT (8, 8, 8, 0, 0, 8, 16, 0)
312};
313#define SDL_PIXELFORMAT_BGR888 &sdl_bgr_888
314
315static const SDL_PixelFormat sdl_rgba_8888 =
316{
317  FORMAT (8, 8, 8, 8, 24, 16, 8, 0)
318};
319#define SDL_PIXELFORMAT_RGBA8888 &sdl_rgba_8888
320
321#endif
322
323static const struct {
324  const char *name;
325  /* Since we declare the pixel formats above for SDL 1, we have the bpp
326     setting, but SDL 2 internally uses larger values for its own memory, so
327     we can't assume the Blackfin pixel format sizes always match SDL.  */
328  int bytes_per_pixel;
329#if HAVE_SDL == 1
330  const SDL_PixelFormat *format;
331#else
332  Uint32 format;
333#endif
334  enum gui_color color;
335} color_spaces[] = {
336  { "rgb565",   2, SDL_PIXELFORMAT_RGB565,   GUI_COLOR_RGB_565,   },
337  { "bgr565",   2, SDL_PIXELFORMAT_BGR565,   GUI_COLOR_BGR_565,   },
338  { "rgb888",   3, SDL_PIXELFORMAT_RGB888,   GUI_COLOR_RGB_888,   },
339  { "bgr888",   3, SDL_PIXELFORMAT_BGR888,   GUI_COLOR_BGR_888,   },
340  { "rgba8888", 4, SDL_PIXELFORMAT_RGBA8888, GUI_COLOR_RGBA_8888, },
341};
342
343enum gui_color bfin_gui_color (const char *color)
344{
345  int i;
346
347  if (!color)
348    goto def;
349
350  for (i = 0; i < ARRAY_SIZE (color_spaces); ++i)
351    if (!strcmp (color, color_spaces[i].name))
352      return color_spaces[i].color;
353
354  /* Pick a random default.  */
355 def:
356  return GUI_COLOR_RGB_888;
357}
358
359static const SDL_PixelFormat *bfin_gui_color_format (enum gui_color color,
360						     int *bytes_per_pixel)
361{
362  int i;
363
364  for (i = 0; i < ARRAY_SIZE (color_spaces); ++i)
365    if (color == color_spaces[i].color)
366      {
367	*bytes_per_pixel = color_spaces[i].bytes_per_pixel;
368#if HAVE_SDL == 1
369	return color_spaces[i].format;
370#else
371	return sdl.AllocFormat (color_spaces[i].format);
372#endif
373      }
374
375  return NULL;
376}
377
378int bfin_gui_color_depth (enum gui_color color)
379{
380  int bytes_per_pixel;
381  const SDL_PixelFormat *format = bfin_gui_color_format (color,
382							 &bytes_per_pixel);
383  return format ? bytes_per_pixel * 8 : 0;
384}
385
386#endif
387