1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
21 #if defined(TARGET_SDL)
23 /* ========================================================================= */
25 /* ========================================================================= */
27 /* functions from SGE library */
28 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
30 /* stuff needed to work around SDL/Windows fullscreen drawing bug */
31 static int fullscreen_width;
32 static int fullscreen_height;
33 static int fullscreen_xoffset;
34 static int fullscreen_yoffset;
35 static int video_xoffset;
36 static int video_yoffset;
38 static void setFullscreenParameters(char *fullscreen_mode_string)
40 struct ScreenModeInfo *fullscreen_mode;
43 fullscreen_mode = get_screen_mode_from_string(fullscreen_mode_string);
45 if (fullscreen_mode == NULL)
48 for (i = 0; video.fullscreen_modes[i].width != -1; i++)
50 if (fullscreen_mode->width == video.fullscreen_modes[i].width &&
51 fullscreen_mode->height == video.fullscreen_modes[i].height)
53 fullscreen_width = fullscreen_mode->width;
54 fullscreen_height = fullscreen_mode->height;
56 fullscreen_xoffset = (fullscreen_width - video.width) / 2;
57 fullscreen_yoffset = (fullscreen_height - video.height) / 2;
64 static void SDLSetWindowIcon(char *basename)
66 /* (setting the window icon on Mac OS X would replace the high-quality
67 dock icon with the currently smaller (and uglier) icon from file) */
69 #if !defined(PLATFORM_MACOSX)
70 char *filename = getCustomImageFilename(basename);
75 Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
80 if ((surface = IMG_Load(filename)) == NULL)
82 Error(ERR_WARN, "IMG_Load() failed: %s", SDL_GetError());
87 /* set transparent color */
88 SDL_SetColorKey(surface, SDL_SRCCOLORKEY,
89 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
91 SDL_WM_SetIcon(surface, NULL);
95 void SDLInitVideoDisplay(void)
97 if (!strEqual(setup.system.sdl_videodriver, ARG_DEFAULT))
98 SDL_putenv(getStringCat2("SDL_VIDEODRIVER=", setup.system.sdl_videodriver));
100 SDL_putenv("SDL_VIDEO_CENTERED=1");
102 /* initialize SDL video */
103 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
104 Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
106 /* set default SDL depth */
107 video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
110 void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
113 static int screen_xy[][2] =
123 /* default: normal game window size */
124 fullscreen_width = video.width;
125 fullscreen_height = video.height;
126 fullscreen_xoffset = 0;
127 fullscreen_yoffset = 0;
129 for (i = 0; screen_xy[i][0] != -1; i++)
131 if (screen_xy[i][0] >= video.width && screen_xy[i][1] >= video.height)
133 fullscreen_width = screen_xy[i][0];
134 fullscreen_height = screen_xy[i][1];
140 fullscreen_xoffset = (fullscreen_width - video.width) / 2;
141 fullscreen_yoffset = (fullscreen_height - video.height) / 2;
143 /* get available hardware supported fullscreen modes */
144 modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_HWSURFACE);
148 /* no screen modes available => no fullscreen mode support */
149 video.fullscreen_available = FALSE;
151 else if (modes == (SDL_Rect **)-1)
153 /* fullscreen resolution is not restricted -- all resolutions available */
154 video.fullscreen_modes = checked_calloc(2 * sizeof(struct ScreenModeInfo));
156 /* use native video buffer size for fullscreen mode */
157 video.fullscreen_modes[0].width = video.width;
158 video.fullscreen_modes[0].height = video.height;
160 video.fullscreen_modes[1].width = -1;
161 video.fullscreen_modes[1].height = -1;
165 /* in this case, a certain number of screen modes is available */
168 for(i = 0; modes[i] != NULL; i++)
170 boolean found_mode = FALSE;
172 /* screen mode is smaller than video buffer size -- skip it */
173 if (modes[i]->w < video.width || modes[i]->h < video.height)
176 if (video.fullscreen_modes != NULL)
177 for (j = 0; video.fullscreen_modes[j].width != -1; j++)
178 if (modes[i]->w == video.fullscreen_modes[j].width &&
179 modes[i]->h == video.fullscreen_modes[j].height)
182 if (found_mode) /* screen mode already stored -- skip it */
185 /* new mode found; add it to list of available fullscreen modes */
189 video.fullscreen_modes = checked_realloc(video.fullscreen_modes,
191 sizeof(struct ScreenModeInfo));
193 video.fullscreen_modes[num_modes - 1].width = modes[i]->w;
194 video.fullscreen_modes[num_modes - 1].height = modes[i]->h;
196 video.fullscreen_modes[num_modes].width = -1;
197 video.fullscreen_modes[num_modes].height = -1;
202 /* no appropriate screen modes available => no fullscreen mode support */
203 video.fullscreen_available = FALSE;
207 /* set window icon */
208 SDLSetWindowIcon(program.sdl_icon_filename);
210 /* open SDL video output device (window or fullscreen mode) */
211 if (!SDLSetVideoMode(backbuffer, fullscreen))
212 Error(ERR_EXIT, "setting video mode failed");
214 /* set window and icon title */
215 SDL_WM_SetCaption(program.window_title, program.window_title);
217 /* SDL cannot directly draw to the visible video framebuffer like X11,
218 but always uses a backbuffer, which is then blitted to the visible
219 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
220 visible video framebuffer with 'SDL_Flip', if the hardware supports
221 this). Therefore do not use an additional backbuffer for drawing, but
222 use a symbolic buffer (distinguishable from the SDL backbuffer) called
223 'window', which indicates that the SDL backbuffer should be updated to
224 the visible video framebuffer when attempting to blit to it.
226 For convenience, it seems to be a good idea to create this symbolic
227 buffer 'window' at the same size as the SDL backbuffer. Although it
228 should never be drawn to directly, it would do no harm nevertheless. */
230 /* create additional (symbolic) buffer for double-buffering */
231 *window = CreateBitmap(video.width, video.height, video.depth);
234 boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
236 boolean success = TRUE;
237 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_FULLSCREEN;
238 int surface_flags_window = SURFACE_FLAGS;
239 SDL_Surface *new_surface = NULL;
241 if (*backbuffer == NULL)
242 *backbuffer = CreateBitmapStruct();
244 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
246 setFullscreenParameters(setup.fullscreen_mode);
248 video_xoffset = fullscreen_xoffset;
249 video_yoffset = fullscreen_yoffset;
251 /* switch display to fullscreen mode, if available */
252 if ((new_surface = SDL_SetVideoMode(fullscreen_width, fullscreen_height,
253 video.depth, surface_flags_fullscreen))
256 /* switching display to fullscreen mode failed */
257 Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
259 /* do not try it again */
260 video.fullscreen_available = FALSE;
266 (*backbuffer)->surface = new_surface;
268 video.fullscreen_enabled = TRUE;
269 video.fullscreen_mode_current = setup.fullscreen_mode;
275 if ((!fullscreen && video.fullscreen_enabled) || new_surface == NULL)
280 /* switch display to window mode */
281 if ((new_surface = SDL_SetVideoMode(video.width, video.height,
282 video.depth, surface_flags_window))
285 /* switching display to window mode failed -- should not happen */
286 Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
292 (*backbuffer)->surface = new_surface;
294 video.fullscreen_enabled = FALSE;
302 void SDLCreateBitmapContent(Bitmap *new_bitmap, int width, int height,
305 SDL_Surface *surface_tmp, *surface_native;
307 if ((surface_tmp = SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth,
310 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
312 if ((surface_native = SDL_DisplayFormat(surface_tmp)) == NULL)
313 Error(ERR_EXIT, "SDL_DisplayFormat() failed: %s", SDL_GetError());
315 SDL_FreeSurface(surface_tmp);
317 new_bitmap->surface = surface_native;
320 void SDLFreeBitmapPointers(Bitmap *bitmap)
323 SDL_FreeSurface(bitmap->surface);
324 if (bitmap->surface_masked)
325 SDL_FreeSurface(bitmap->surface_masked);
326 bitmap->surface = NULL;
327 bitmap->surface_masked = NULL;
330 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
331 int src_x, int src_y, int width, int height,
332 int dst_x, int dst_y, int mask_mode)
334 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
335 SDL_Rect src_rect, dst_rect;
337 if (src_bitmap == backbuffer)
339 src_x += video_xoffset;
340 src_y += video_yoffset;
348 if (dst_bitmap == backbuffer || dst_bitmap == window)
350 dst_x += video_xoffset;
351 dst_y += video_yoffset;
359 if (src_bitmap != backbuffer || dst_bitmap != window)
360 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
361 src_bitmap->surface_masked : src_bitmap->surface),
362 &src_rect, real_dst_bitmap->surface, &dst_rect);
364 if (dst_bitmap == window)
365 SDL_UpdateRect(backbuffer->surface, dst_x, dst_y, width, height);
368 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
371 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
374 if (dst_bitmap == backbuffer || dst_bitmap == window)
385 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
387 if (dst_bitmap == window)
388 SDL_UpdateRect(backbuffer->surface, x, y, width, height);
391 void SDLFadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
392 int fade_mode, int fade_delay, int post_delay,
393 void (*draw_border_function)(void))
395 static boolean initialization_needed = TRUE;
396 static SDL_Surface *surface_source = NULL;
397 static SDL_Surface *surface_target = NULL;
398 static SDL_Surface *surface_black = NULL;
399 SDL_Surface *surface_screen = backbuffer->surface;
400 SDL_Surface *surface_cross = (bitmap_cross ? bitmap_cross->surface : NULL);
401 SDL_Rect src_rect, dst_rect;
402 int src_x = x, src_y = y;
403 int dst_x = x, dst_y = y;
404 unsigned int time_last, time_current;
411 dst_x += video_xoffset;
412 dst_y += video_yoffset;
416 dst_rect.w = width; /* (ignored) */
417 dst_rect.h = height; /* (ignored) */
419 if (initialization_needed)
421 unsigned int flags = SDL_SRCALPHA;
423 /* use same surface type as screen surface */
424 if ((surface_screen->flags & SDL_HWSURFACE))
425 flags |= SDL_HWSURFACE;
427 flags |= SDL_SWSURFACE;
429 /* create surface for temporary copy of screen buffer (source) */
430 if ((surface_source =
431 SDL_CreateRGBSurface(flags,
434 surface_screen->format->BitsPerPixel,
435 surface_screen->format->Rmask,
436 surface_screen->format->Gmask,
437 surface_screen->format->Bmask,
438 surface_screen->format->Amask)) == NULL)
439 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
441 /* create surface for cross-fading screen buffer (target) */
442 if ((surface_target =
443 SDL_CreateRGBSurface(flags,
446 surface_screen->format->BitsPerPixel,
447 surface_screen->format->Rmask,
448 surface_screen->format->Gmask,
449 surface_screen->format->Bmask,
450 surface_screen->format->Amask)) == NULL)
451 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
453 /* create black surface for fading from/to black */
455 SDL_CreateRGBSurface(flags,
458 surface_screen->format->BitsPerPixel,
459 surface_screen->format->Rmask,
460 surface_screen->format->Gmask,
461 surface_screen->format->Bmask,
462 surface_screen->format->Amask)) == NULL)
463 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
465 /* completely fill the surface with black color pixels */
466 SDL_FillRect(surface_black, NULL,
467 SDL_MapRGB(surface_screen->format, 0, 0, 0));
469 initialization_needed = FALSE;
472 /* copy source and target surfaces to temporary surfaces for fading */
473 if (fade_mode & FADE_TYPE_TRANSFORM)
475 SDL_BlitSurface(surface_cross, &src_rect, surface_source, &src_rect);
476 SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
478 else if (fade_mode & FADE_TYPE_FADE_IN)
480 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
481 SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
483 else /* FADE_TYPE_FADE_OUT */
485 SDL_BlitSurface(surface_screen, &dst_rect, surface_source, &src_rect);
486 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
489 time_current = SDL_GetTicks();
491 if (fade_mode == FADE_MODE_MELT)
493 boolean done = FALSE;
495 int melt_columns = width / melt_pixels;
496 int ypos[melt_columns];
497 int max_steps = height / 8 + 32;
502 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
503 SDL_SetAlpha(surface_target, 0, 0); /* disable alpha blending */
505 ypos[0] = -GetSimpleRandom(16);
507 for (i = 1 ; i < melt_columns; i++)
509 int r = GetSimpleRandom(3) - 1; /* randomly choose from { -1, 0, -1 } */
511 ypos[i] = ypos[i - 1] + r;
524 time_last = time_current;
525 time_current = SDL_GetTicks();
526 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
527 steps_final = MIN(MAX(0, steps), max_steps);
531 done = (steps_done >= steps_final);
533 for (i = 0 ; i < melt_columns; i++)
541 else if (ypos[i] < height)
546 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
548 if (ypos[i] + dy >= height)
549 dy = height - ypos[i];
551 /* copy part of (appearing) target surface to upper area */
552 src_rect.x = src_x + i * melt_pixels;
553 // src_rect.y = src_y + ypos[i];
555 src_rect.w = melt_pixels;
557 src_rect.h = ypos[i] + dy;
559 dst_rect.x = dst_x + i * melt_pixels;
560 // dst_rect.y = dst_y + ypos[i];
563 if (steps_done >= steps_final)
564 SDL_BlitSurface(surface_target, &src_rect,
565 surface_screen, &dst_rect);
569 /* copy part of (disappearing) source surface to lower area */
570 src_rect.x = src_x + i * melt_pixels;
572 src_rect.w = melt_pixels;
573 src_rect.h = height - ypos[i];
575 dst_rect.x = dst_x + i * melt_pixels;
576 dst_rect.y = dst_y + ypos[i];
578 if (steps_done >= steps_final)
579 SDL_BlitSurface(surface_source, &src_rect,
580 surface_screen, &dst_rect);
586 src_rect.x = src_x + i * melt_pixels;
588 src_rect.w = melt_pixels;
591 dst_rect.x = dst_x + i * melt_pixels;
594 if (steps_done >= steps_final)
595 SDL_BlitSurface(surface_target, &src_rect,
596 surface_screen, &dst_rect);
600 if (steps_done >= steps_final)
602 if (draw_border_function != NULL)
603 draw_border_function();
605 SDL_UpdateRect(surface_screen, dst_x, dst_y, width, height);
614 for (alpha = 0.0; alpha < 255.0;)
616 time_last = time_current;
617 time_current = SDL_GetTicks();
618 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
619 alpha_final = MIN(MAX(0, alpha), 255);
621 /* draw existing (source) image to screen buffer */
622 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
624 /* draw new (target) image to screen buffer using alpha blending */
625 SDL_SetAlpha(surface_target, SDL_SRCALPHA, alpha_final);
626 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
628 if (draw_border_function != NULL)
629 draw_border_function();
632 /* only update the region of the screen that is affected from fading */
633 SDL_UpdateRect(surface_screen, dst_x, dst_y, width, height);
635 SDL_Flip(surface_screen);
643 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
644 int to_x, int to_y, Uint32 color)
646 SDL_Surface *surface = dst_bitmap->surface;
650 swap_numbers(&from_x, &to_x);
653 swap_numbers(&from_y, &to_y);
657 rect.w = (to_x - from_x + 1);
658 rect.h = (to_y - from_y + 1);
660 if (dst_bitmap == backbuffer || dst_bitmap == window)
662 rect.x += video_xoffset;
663 rect.y += video_yoffset;
666 SDL_FillRect(surface, &rect, color);
669 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
670 int to_x, int to_y, Uint32 color)
672 if (dst_bitmap == backbuffer || dst_bitmap == window)
674 from_x += video_xoffset;
675 from_y += video_yoffset;
676 to_x += video_xoffset;
677 to_y += video_yoffset;
680 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
684 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
685 int num_points, Uint32 color)
690 for (i = 0; i < num_points - 1; i++)
692 for (x = 0; x < line_width; x++)
694 for (y = 0; y < line_width; y++)
696 int dx = x - line_width / 2;
697 int dy = y - line_width / 2;
699 if ((x == 0 && y == 0) ||
700 (x == 0 && y == line_width - 1) ||
701 (x == line_width - 1 && y == 0) ||
702 (x == line_width - 1 && y == line_width - 1))
705 sge_Line(surface, points[i].x + dx, points[i].y + dy,
706 points[i+1].x + dx, points[i+1].y + dy, color);
713 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
715 SDL_Surface *surface = src_bitmap->surface;
717 if (src_bitmap == backbuffer || src_bitmap == window)
723 switch (surface->format->BytesPerPixel)
725 case 1: /* assuming 8-bpp */
727 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
731 case 2: /* probably 15-bpp or 16-bpp */
733 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
737 case 3: /* slow 24-bpp mode; usually not used */
739 /* does this work? */
740 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
744 shift = surface->format->Rshift;
745 color |= *(pix + shift / 8) >> shift;
746 shift = surface->format->Gshift;
747 color |= *(pix + shift / 8) >> shift;
748 shift = surface->format->Bshift;
749 color |= *(pix + shift / 8) >> shift;
755 case 4: /* probably 32-bpp */
757 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
766 /* ========================================================================= */
767 /* The following functions were taken from the SGE library */
768 /* (SDL Graphics Extension Library) by Anders Lindström */
769 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html */
770 /* ========================================================================= */
772 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
774 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
776 switch (surface->format->BytesPerPixel)
781 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
787 /* Probably 15-bpp or 16-bpp */
788 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
794 /* Slow 24-bpp mode, usually not used */
798 /* Gack - slow, but endian correct */
799 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
800 shift = surface->format->Rshift;
801 *(pix+shift/8) = color>>shift;
802 shift = surface->format->Gshift;
803 *(pix+shift/8) = color>>shift;
804 shift = surface->format->Bshift;
805 *(pix+shift/8) = color>>shift;
811 /* Probably 32-bpp */
812 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
819 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
820 Uint8 R, Uint8 G, Uint8 B)
822 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
825 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
827 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
830 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
832 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
835 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
840 /* Gack - slow, but endian correct */
841 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
842 shift = surface->format->Rshift;
843 *(pix+shift/8) = color>>shift;
844 shift = surface->format->Gshift;
845 *(pix+shift/8) = color>>shift;
846 shift = surface->format->Bshift;
847 *(pix+shift/8) = color>>shift;
850 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
852 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
855 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
857 switch (dest->format->BytesPerPixel)
860 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
864 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
868 _PutPixel24(dest,x,y,color);
872 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
877 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
879 if (SDL_MUSTLOCK(surface))
881 if (SDL_LockSurface(surface) < 0)
887 _PutPixel(surface, x, y, color);
889 if (SDL_MUSTLOCK(surface))
891 SDL_UnlockSurface(surface);
895 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
896 Uint8 r, Uint8 g, Uint8 b)
898 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
901 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
903 if (y >= 0 && y <= dest->h - 1)
905 switch (dest->format->BytesPerPixel)
908 return y*dest->pitch;
912 return y*dest->pitch/2;
916 return y*dest->pitch;
920 return y*dest->pitch/4;
928 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
930 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
932 switch (surface->format->BytesPerPixel)
937 *((Uint8 *)surface->pixels + ypitch + x) = color;
943 /* Probably 15-bpp or 16-bpp */
944 *((Uint16 *)surface->pixels + ypitch + x) = color;
950 /* Slow 24-bpp mode, usually not used */
954 /* Gack - slow, but endian correct */
955 pix = (Uint8 *)surface->pixels + ypitch + x*3;
956 shift = surface->format->Rshift;
957 *(pix+shift/8) = color>>shift;
958 shift = surface->format->Gshift;
959 *(pix+shift/8) = color>>shift;
960 shift = surface->format->Bshift;
961 *(pix+shift/8) = color>>shift;
967 /* Probably 32-bpp */
968 *((Uint32 *)surface->pixels + ypitch + x) = color;
975 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
980 if (SDL_MUSTLOCK(Surface))
982 if (SDL_LockSurface(Surface) < 0)
995 /* Do the clipping */
996 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1000 if (x2 > Surface->w - 1)
1001 x2 = Surface->w - 1;
1008 SDL_FillRect(Surface, &l, Color);
1010 if (SDL_MUSTLOCK(Surface))
1012 SDL_UnlockSurface(Surface);
1016 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1017 Uint8 R, Uint8 G, Uint8 B)
1019 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1022 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
1033 /* Do the clipping */
1034 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1038 if (x2 > Surface->w - 1)
1039 x2 = Surface->w - 1;
1046 SDL_FillRect(Surface, &l, Color);
1049 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1054 if (SDL_MUSTLOCK(Surface))
1056 if (SDL_LockSurface(Surface) < 0)
1069 /* Do the clipping */
1070 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1074 if (y2 > Surface->h - 1)
1075 y2 = Surface->h - 1;
1082 SDL_FillRect(Surface, &l, Color);
1084 if (SDL_MUSTLOCK(Surface))
1086 SDL_UnlockSurface(Surface);
1090 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1091 Uint8 R, Uint8 G, Uint8 B)
1093 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1096 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
1107 /* Do the clipping */
1108 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1112 if (y2 > Surface->h - 1)
1113 y2 = Surface->h - 1;
1120 SDL_FillRect(Surface, &l, Color);
1123 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1124 Sint16 x2, Sint16 y2, Uint32 Color,
1125 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1128 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1133 sdx = (dx < 0) ? -1 : 1;
1134 sdy = (dy < 0) ? -1 : 1;
1146 for (x = 0; x < dx; x++)
1148 Callback(Surface, px, py, Color);
1162 for (y = 0; y < dy; y++)
1164 Callback(Surface, px, py, Color);
1178 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1179 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1180 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1183 sge_DoLine(Surface, X1, Y1, X2, Y2,
1184 SDL_MapRGB(Surface->format, R, G, B), Callback);
1187 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1190 if (SDL_MUSTLOCK(Surface))
1192 if (SDL_LockSurface(Surface) < 0)
1197 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1199 /* unlock the display */
1200 if (SDL_MUSTLOCK(Surface))
1202 SDL_UnlockSurface(Surface);
1206 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1207 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1209 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1212 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1214 if (dst_bitmap == backbuffer || dst_bitmap == window)
1220 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1225 -----------------------------------------------------------------------------
1226 quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1227 -----------------------------------------------------------------------------
1230 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1231 int width, int height, Uint32 color)
1235 for (y = src_y; y < src_y + height; y++)
1237 for (x = src_x; x < src_x + width; x++)
1239 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1241 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1246 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1247 int src_x, int src_y, int width, int height,
1248 int dst_x, int dst_y)
1252 for (y = 0; y < height; y++)
1254 for (x = 0; x < width; x++)
1256 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1258 if (pixel != BLACK_PIXEL)
1259 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1265 /* ========================================================================= */
1266 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
1267 /* (Rotozoomer) by Andreas Schiffler */
1268 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html */
1269 /* ========================================================================= */
1272 -----------------------------------------------------------------------------
1275 zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1276 -----------------------------------------------------------------------------
1287 int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1290 tColorRGBA *sp, *csp, *dp;
1294 sp = csp = (tColorRGBA *) src->pixels;
1295 dp = (tColorRGBA *) dst->pixels;
1296 sgap = src->pitch - src->w * 4;
1297 dgap = dst->pitch - dst->w * 4;
1299 for (y = 0; y < dst->h; y++)
1303 for (x = 0; x < dst->w; x++)
1305 tColorRGBA *sp0 = sp;
1306 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1307 tColorRGBA *sp00 = &sp0[0];
1308 tColorRGBA *sp01 = &sp0[1];
1309 tColorRGBA *sp10 = &sp1[0];
1310 tColorRGBA *sp11 = &sp1[1];
1313 /* create new color pixel from all four source color pixels */
1314 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1315 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1316 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1317 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1322 /* advance source pointers */
1325 /* advance destination pointer */
1329 /* advance source pointer */
1330 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1332 /* advance destination pointers */
1333 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1339 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1341 int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1342 tColorRGBA *sp, *csp, *dp;
1345 /* use specialized zoom function when scaling down to exactly half size */
1346 if (src->w == 2 * dst->w &&
1347 src->h == 2 * dst->h)
1348 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1350 /* variable setup */
1351 sx = (int) (65536.0 * (float) src->w / (float) dst->w);
1352 sy = (int) (65536.0 * (float) src->h / (float) dst->h);
1354 /* allocate memory for row increments */
1355 sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1356 say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1358 /* precalculate row increments */
1361 for (x = 0; x <= dst->w; x++)
1371 for (y = 0; y <= dst->h; y++)
1380 sp = csp = (tColorRGBA *) src->pixels;
1381 dp = (tColorRGBA *) dst->pixels;
1382 sgap = src->pitch - src->w * 4;
1383 dgap = dst->pitch - dst->w * 4;
1386 for (y = 0; y < dst->h; y++)
1391 for (x = 0; x < dst->w; x++)
1396 /* advance source pointers */
1398 sp += (*csax >> 16);
1400 /* advance destination pointer */
1404 /* advance source pointer */
1406 csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
1408 /* advance destination pointers */
1409 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1419 -----------------------------------------------------------------------------
1422 zoomes 8 bit palette/Y 'src' surface to 'dst' surface
1423 -----------------------------------------------------------------------------
1426 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
1428 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1429 Uint8 *sp, *dp, *csp;
1432 /* variable setup */
1433 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
1434 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
1436 /* allocate memory for row increments */
1437 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
1438 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
1440 /* precalculate row increments */
1443 for (x = 0; x < dst->w; x++)
1446 *csax = (csx >> 16);
1453 for (y = 0; y < dst->h; y++)
1456 *csay = (csy >> 16);
1463 for (x = 0; x < dst->w; x++)
1471 for (y = 0; y < dst->h; y++)
1478 sp = csp = (Uint8 *) src->pixels;
1479 dp = (Uint8 *) dst->pixels;
1480 dgap = dst->pitch - dst->w;
1484 for (y = 0; y < dst->h; y++)
1488 for (x = 0; x < dst->w; x++)
1493 /* advance source pointers */
1497 /* advance destination pointer */
1501 /* advance source pointer (for row) */
1502 csp += ((*csay) * src->pitch);
1505 /* advance destination pointers */
1516 -----------------------------------------------------------------------------
1519 Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
1520 'zoomx' and 'zoomy' are scaling factors for width and height.
1521 If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
1522 into a 32bit RGBA format on the fly.
1523 -----------------------------------------------------------------------------
1526 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
1528 SDL_Surface *zoom_src = NULL;
1529 SDL_Surface *zoom_dst = NULL;
1530 boolean is_converted = FALSE;
1537 /* determine if source surface is 32 bit or 8 bit */
1538 is_32bit = (src->format->BitsPerPixel == 32);
1540 if (is_32bit || src->format->BitsPerPixel == 8)
1542 /* use source surface 'as is' */
1547 /* new source surface is 32 bit with a defined RGB ordering */
1548 zoom_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1549 0x000000ff, 0x0000ff00, 0x00ff0000, 0);
1550 SDL_BlitSurface(src, NULL, zoom_src, NULL);
1552 is_converted = TRUE;
1555 /* allocate surface to completely contain the zoomed surface */
1558 /* target surface is 32 bit with source RGBA/ABGR ordering */
1559 zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 32,
1560 zoom_src->format->Rmask,
1561 zoom_src->format->Gmask,
1562 zoom_src->format->Bmask, 0);
1566 /* target surface is 8 bit */
1567 zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 8,
1571 /* lock source surface */
1572 SDL_LockSurface(zoom_src);
1574 /* check which kind of surface we have */
1577 /* call the 32 bit transformation routine to do the zooming */
1578 zoomSurfaceRGBA(zoom_src, zoom_dst);
1583 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
1584 zoom_dst->format->palette->colors[i] =
1585 zoom_src->format->palette->colors[i];
1586 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
1588 /* call the 8 bit transformation routine to do the zooming */
1589 zoomSurfaceY(zoom_src, zoom_dst);
1592 /* unlock source surface */
1593 SDL_UnlockSurface(zoom_src);
1595 /* free temporary surface */
1597 SDL_FreeSurface(zoom_src);
1599 /* return destination surface */
1603 void SDLZoomBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap)
1605 SDL_Surface *sdl_surface_tmp;
1606 int dst_width = dst_bitmap->width;
1607 int dst_height = dst_bitmap->height;
1609 /* throw away old destination surface */
1610 SDL_FreeSurface(dst_bitmap->surface);
1612 /* create zoomed temporary surface from source surface */
1613 sdl_surface_tmp = zoomSurface(src_bitmap->surface, dst_width, dst_height);
1615 /* create native format destination surface from zoomed temporary surface */
1616 dst_bitmap->surface = SDL_DisplayFormat(sdl_surface_tmp);
1618 /* free temporary surface */
1619 SDL_FreeSurface(sdl_surface_tmp);
1623 /* ========================================================================= */
1624 /* load image to bitmap */
1625 /* ========================================================================= */
1627 Bitmap *SDLLoadImage(char *filename)
1629 Bitmap *new_bitmap = CreateBitmapStruct();
1630 SDL_Surface *sdl_image_tmp;
1632 /* load image to temporary surface */
1633 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
1635 SetError("IMG_Load(): %s", SDL_GetError());
1640 UPDATE_BUSY_STATE();
1642 /* create native non-transparent surface for current image */
1643 if ((new_bitmap->surface = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1645 SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1650 UPDATE_BUSY_STATE();
1652 /* create native transparent surface for current image */
1653 SDL_SetColorKey(sdl_image_tmp, SDL_SRCCOLORKEY,
1654 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
1655 if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1657 SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1662 UPDATE_BUSY_STATE();
1664 /* free temporary surface */
1665 SDL_FreeSurface(sdl_image_tmp);
1667 new_bitmap->width = new_bitmap->surface->w;
1668 new_bitmap->height = new_bitmap->surface->h;
1674 /* ------------------------------------------------------------------------- */
1675 /* custom cursor fuctions */
1676 /* ------------------------------------------------------------------------- */
1678 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
1680 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
1681 cursor_info->width, cursor_info->height,
1682 cursor_info->hot_x, cursor_info->hot_y);
1685 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
1687 static struct MouseCursorInfo *last_cursor_info = NULL;
1688 static struct MouseCursorInfo *last_cursor_info2 = NULL;
1689 static SDL_Cursor *cursor_default = NULL;
1690 static SDL_Cursor *cursor_current = NULL;
1692 /* if invoked for the first time, store the SDL default cursor */
1693 if (cursor_default == NULL)
1694 cursor_default = SDL_GetCursor();
1696 /* only create new cursor if cursor info (custom only) has changed */
1697 if (cursor_info != NULL && cursor_info != last_cursor_info)
1699 cursor_current = create_cursor(cursor_info);
1700 last_cursor_info = cursor_info;
1703 /* only set new cursor if cursor info (custom or NULL) has changed */
1704 if (cursor_info != last_cursor_info2)
1705 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
1707 last_cursor_info2 = cursor_info;
1711 /* ========================================================================= */
1712 /* audio functions */
1713 /* ========================================================================= */
1715 void SDLOpenAudio(void)
1717 if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
1718 SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
1720 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
1722 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
1726 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
1727 AUDIO_NUM_CHANNELS_STEREO,
1728 setup.system.audio_fragment_size) < 0)
1730 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
1734 audio.sound_available = TRUE;
1735 audio.music_available = TRUE;
1736 audio.loops_available = TRUE;
1737 audio.sound_enabled = TRUE;
1739 /* set number of available mixer channels */
1740 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
1741 audio.music_channel = MUSIC_CHANNEL;
1742 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
1744 Mixer_InitChannels();
1747 void SDLCloseAudio(void)
1750 Mix_HaltChannel(-1);
1753 SDL_QuitSubSystem(SDL_INIT_AUDIO);
1757 /* ========================================================================= */
1758 /* event functions */
1759 /* ========================================================================= */
1761 void SDLNextEvent(Event *event)
1763 SDL_WaitEvent(event);
1765 if (event->type == EVENT_BUTTONPRESS ||
1766 event->type == EVENT_BUTTONRELEASE)
1768 if (((ButtonEvent *)event)->x > video_xoffset)
1769 ((ButtonEvent *)event)->x -= video_xoffset;
1771 ((ButtonEvent *)event)->x = 0;
1772 if (((ButtonEvent *)event)->y > video_yoffset)
1773 ((ButtonEvent *)event)->y -= video_yoffset;
1775 ((ButtonEvent *)event)->y = 0;
1777 else if (event->type == EVENT_MOTIONNOTIFY)
1779 if (((MotionEvent *)event)->x > video_xoffset)
1780 ((MotionEvent *)event)->x -= video_xoffset;
1782 ((MotionEvent *)event)->x = 0;
1783 if (((MotionEvent *)event)->y > video_yoffset)
1784 ((MotionEvent *)event)->y -= video_yoffset;
1786 ((MotionEvent *)event)->y = 0;
1791 /* ========================================================================= */
1792 /* joystick functions */
1793 /* ========================================================================= */
1795 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
1796 static int sdl_js_axis[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1797 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1799 static boolean SDLOpenJoystick(int nr)
1801 if (nr < 0 || nr > MAX_PLAYERS)
1804 return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
1807 static void SDLCloseJoystick(int nr)
1809 if (nr < 0 || nr > MAX_PLAYERS)
1812 SDL_JoystickClose(sdl_joystick[nr]);
1815 static boolean SDLCheckJoystickOpened(int nr)
1817 if (nr < 0 || nr > MAX_PLAYERS)
1820 return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
1823 void HandleJoystickEvent(Event *event)
1827 case SDL_JOYAXISMOTION:
1828 if (event->jaxis.axis < 2)
1829 sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
1832 case SDL_JOYBUTTONDOWN:
1833 if (event->jbutton.button < 2)
1834 sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
1837 case SDL_JOYBUTTONUP:
1838 if (event->jbutton.button < 2)
1839 sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
1847 void SDLInitJoysticks()
1849 static boolean sdl_joystick_subsystem_initialized = FALSE;
1850 boolean print_warning = !sdl_joystick_subsystem_initialized;
1853 if (!sdl_joystick_subsystem_initialized)
1855 sdl_joystick_subsystem_initialized = TRUE;
1857 if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
1859 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
1864 for (i = 0; i < MAX_PLAYERS; i++)
1866 /* get configured joystick for this player */
1867 char *device_name = setup.input[i].joy.device_name;
1868 int joystick_nr = getJoystickNrFromDeviceName(device_name);
1870 if (joystick_nr >= SDL_NumJoysticks())
1872 if (setup.input[i].use_joystick && print_warning)
1873 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
1878 /* misuse joystick file descriptor variable to store joystick number */
1879 joystick.fd[i] = joystick_nr;
1881 if (joystick_nr == -1)
1884 /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
1885 if (SDLCheckJoystickOpened(joystick_nr))
1886 SDLCloseJoystick(joystick_nr);
1888 if (!setup.input[i].use_joystick)
1891 if (!SDLOpenJoystick(joystick_nr))
1894 Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
1899 joystick.status = JOYSTICK_ACTIVATED;
1903 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1905 if (nr < 0 || nr >= MAX_PLAYERS)
1909 *x = sdl_js_axis[nr][0];
1911 *y = sdl_js_axis[nr][1];
1914 *b1 = sdl_js_button[nr][0];
1916 *b2 = sdl_js_button[nr][1];
1921 #endif /* TARGET_SDL */