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;
413 dst_x += video_xoffset;
414 dst_y += video_yoffset;
418 dst_rect.w = width; /* (ignored) */
419 dst_rect.h = height; /* (ignored) */
421 if (initialization_needed)
423 unsigned int flags = SDL_SRCALPHA;
425 /* use same surface type as screen surface */
426 if ((surface_screen->flags & SDL_HWSURFACE))
427 flags |= SDL_HWSURFACE;
429 flags |= SDL_SWSURFACE;
431 /* create surface for temporary copy of screen buffer (source) */
432 if ((surface_source =
433 SDL_CreateRGBSurface(flags,
436 surface_screen->format->BitsPerPixel,
437 surface_screen->format->Rmask,
438 surface_screen->format->Gmask,
439 surface_screen->format->Bmask,
440 surface_screen->format->Amask)) == NULL)
441 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
443 /* create surface for cross-fading screen buffer (target) */
444 if ((surface_target =
445 SDL_CreateRGBSurface(flags,
448 surface_screen->format->BitsPerPixel,
449 surface_screen->format->Rmask,
450 surface_screen->format->Gmask,
451 surface_screen->format->Bmask,
452 surface_screen->format->Amask)) == NULL)
453 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
455 /* create black surface for fading from/to black */
457 SDL_CreateRGBSurface(flags,
460 surface_screen->format->BitsPerPixel,
461 surface_screen->format->Rmask,
462 surface_screen->format->Gmask,
463 surface_screen->format->Bmask,
464 surface_screen->format->Amask)) == NULL)
465 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
467 /* completely fill the surface with black color pixels */
468 SDL_FillRect(surface_black, NULL,
469 SDL_MapRGB(surface_screen->format, 0, 0, 0));
471 initialization_needed = FALSE;
474 /* copy source and target surfaces to temporary surfaces for fading */
475 if (fade_mode & FADE_TYPE_TRANSFORM)
477 SDL_BlitSurface(surface_cross, &src_rect, surface_source, &src_rect);
478 SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
480 else if (fade_mode & FADE_TYPE_FADE_IN)
482 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
483 SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
485 else /* FADE_TYPE_FADE_OUT */
487 SDL_BlitSurface(surface_screen, &dst_rect, surface_source, &src_rect);
488 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
491 time_current = SDL_GetTicks();
493 if (fade_mode == FADE_MODE_MELT)
495 boolean done = FALSE;
497 int melt_columns = width / melt_pixels;
498 int ypos[melt_columns];
499 int max_steps = height / 8 + 32;
503 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
504 SDL_SetAlpha(surface_target, 0, 0); /* disable alpha blending */
506 ypos[0] = -GetSimpleRandom(16);
508 for (i = 1 ; i < melt_columns; i++)
510 int r = GetSimpleRandom(3) - 1; /* randomly choose from { -1, 0, -1 } */
512 ypos[i] = ypos[i - 1] + r;
526 time_last = time_current;
527 time_current = SDL_GetTicks();
528 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
529 steps_final = MIN(MAX(0, steps), max_steps);
533 done = (steps_done >= steps_final);
535 for (i = 0 ; i < melt_columns; i++)
543 else if (ypos[i] < height)
548 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
550 if (ypos[i] + dy >= height)
551 dy = height - ypos[i];
553 /* copy part of (appearing) target surface to upper area */
554 src_rect.x = src_x + i * melt_pixels;
555 // src_rect.y = src_y + ypos[i];
557 src_rect.w = melt_pixels;
559 src_rect.h = ypos[i] + dy;
561 dst_rect.x = dst_x + i * melt_pixels;
562 // dst_rect.y = dst_y + ypos[i];
565 if (steps_done >= steps_final)
566 SDL_BlitSurface(surface_target, &src_rect,
567 surface_screen, &dst_rect);
571 /* copy part of (disappearing) source surface to lower area */
572 src_rect.x = src_x + i * melt_pixels;
574 src_rect.w = melt_pixels;
575 src_rect.h = height - ypos[i];
577 dst_rect.x = dst_x + i * melt_pixels;
578 dst_rect.y = dst_y + ypos[i];
580 if (steps_done >= steps_final)
581 SDL_BlitSurface(surface_source, &src_rect,
582 surface_screen, &dst_rect);
588 src_rect.x = src_x + i * melt_pixels;
590 src_rect.w = melt_pixels;
593 dst_rect.x = dst_x + i * melt_pixels;
596 if (steps_done >= steps_final)
597 SDL_BlitSurface(surface_target, &src_rect,
598 surface_screen, &dst_rect);
602 if (steps_done >= steps_final)
604 if (draw_border_function != NULL)
605 draw_border_function();
607 SDL_UpdateRect(surface_screen, dst_x, dst_y, width, height);
613 for (alpha = 0.0; alpha < 255.0;)
615 time_last = time_current;
616 time_current = SDL_GetTicks();
617 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
618 alpha_final = MIN(MAX(0, alpha), 255);
620 /* draw existing (source) image to screen buffer */
621 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
623 /* draw new (target) image to screen buffer using alpha blending */
624 SDL_SetAlpha(surface_target, SDL_SRCALPHA, alpha_final);
625 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
627 if (draw_border_function != NULL)
628 draw_border_function();
631 /* only update the region of the screen that is affected from fading */
632 SDL_UpdateRect(surface_screen, dst_x, dst_y, width, height);
634 SDL_Flip(surface_screen);
642 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
643 int to_x, int to_y, Uint32 color)
645 SDL_Surface *surface = dst_bitmap->surface;
649 swap_numbers(&from_x, &to_x);
652 swap_numbers(&from_y, &to_y);
656 rect.w = (to_x - from_x + 1);
657 rect.h = (to_y - from_y + 1);
659 if (dst_bitmap == backbuffer || dst_bitmap == window)
661 rect.x += video_xoffset;
662 rect.y += video_yoffset;
665 SDL_FillRect(surface, &rect, color);
668 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
669 int to_x, int to_y, Uint32 color)
671 if (dst_bitmap == backbuffer || dst_bitmap == window)
673 from_x += video_xoffset;
674 from_y += video_yoffset;
675 to_x += video_xoffset;
676 to_y += video_yoffset;
679 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
683 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
684 int num_points, Uint32 color)
689 for (i = 0; i < num_points - 1; i++)
691 for (x = 0; x < line_width; x++)
693 for (y = 0; y < line_width; y++)
695 int dx = x - line_width / 2;
696 int dy = y - line_width / 2;
698 if ((x == 0 && y == 0) ||
699 (x == 0 && y == line_width - 1) ||
700 (x == line_width - 1 && y == 0) ||
701 (x == line_width - 1 && y == line_width - 1))
704 sge_Line(surface, points[i].x + dx, points[i].y + dy,
705 points[i+1].x + dx, points[i+1].y + dy, color);
712 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
714 SDL_Surface *surface = src_bitmap->surface;
716 if (src_bitmap == backbuffer || src_bitmap == window)
722 switch (surface->format->BytesPerPixel)
724 case 1: /* assuming 8-bpp */
726 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
730 case 2: /* probably 15-bpp or 16-bpp */
732 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
736 case 3: /* slow 24-bpp mode; usually not used */
738 /* does this work? */
739 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
743 shift = surface->format->Rshift;
744 color |= *(pix + shift / 8) >> shift;
745 shift = surface->format->Gshift;
746 color |= *(pix + shift / 8) >> shift;
747 shift = surface->format->Bshift;
748 color |= *(pix + shift / 8) >> shift;
754 case 4: /* probably 32-bpp */
756 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
765 /* ========================================================================= */
766 /* The following functions were taken from the SGE library */
767 /* (SDL Graphics Extension Library) by Anders Lindström */
768 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html */
769 /* ========================================================================= */
771 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
773 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
775 switch (surface->format->BytesPerPixel)
780 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
786 /* Probably 15-bpp or 16-bpp */
787 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
793 /* Slow 24-bpp mode, usually not used */
797 /* Gack - slow, but endian correct */
798 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
799 shift = surface->format->Rshift;
800 *(pix+shift/8) = color>>shift;
801 shift = surface->format->Gshift;
802 *(pix+shift/8) = color>>shift;
803 shift = surface->format->Bshift;
804 *(pix+shift/8) = color>>shift;
810 /* Probably 32-bpp */
811 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
818 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
819 Uint8 R, Uint8 G, Uint8 B)
821 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
824 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
826 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
829 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
831 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
834 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
839 /* Gack - slow, but endian correct */
840 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
841 shift = surface->format->Rshift;
842 *(pix+shift/8) = color>>shift;
843 shift = surface->format->Gshift;
844 *(pix+shift/8) = color>>shift;
845 shift = surface->format->Bshift;
846 *(pix+shift/8) = color>>shift;
849 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
851 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
854 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
856 switch (dest->format->BytesPerPixel)
859 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
863 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
867 _PutPixel24(dest,x,y,color);
871 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
876 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
878 if (SDL_MUSTLOCK(surface))
880 if (SDL_LockSurface(surface) < 0)
886 _PutPixel(surface, x, y, color);
888 if (SDL_MUSTLOCK(surface))
890 SDL_UnlockSurface(surface);
894 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
895 Uint8 r, Uint8 g, Uint8 b)
897 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
900 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
902 if (y >= 0 && y <= dest->h - 1)
904 switch (dest->format->BytesPerPixel)
907 return y*dest->pitch;
911 return y*dest->pitch/2;
915 return y*dest->pitch;
919 return y*dest->pitch/4;
927 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
929 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
931 switch (surface->format->BytesPerPixel)
936 *((Uint8 *)surface->pixels + ypitch + x) = color;
942 /* Probably 15-bpp or 16-bpp */
943 *((Uint16 *)surface->pixels + ypitch + x) = color;
949 /* Slow 24-bpp mode, usually not used */
953 /* Gack - slow, but endian correct */
954 pix = (Uint8 *)surface->pixels + ypitch + x*3;
955 shift = surface->format->Rshift;
956 *(pix+shift/8) = color>>shift;
957 shift = surface->format->Gshift;
958 *(pix+shift/8) = color>>shift;
959 shift = surface->format->Bshift;
960 *(pix+shift/8) = color>>shift;
966 /* Probably 32-bpp */
967 *((Uint32 *)surface->pixels + ypitch + x) = color;
974 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
979 if (SDL_MUSTLOCK(Surface))
981 if (SDL_LockSurface(Surface) < 0)
994 /* Do the clipping */
995 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
999 if (x2 > Surface->w - 1)
1000 x2 = Surface->w - 1;
1007 SDL_FillRect(Surface, &l, Color);
1009 if (SDL_MUSTLOCK(Surface))
1011 SDL_UnlockSurface(Surface);
1015 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1016 Uint8 R, Uint8 G, Uint8 B)
1018 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1021 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
1032 /* Do the clipping */
1033 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1037 if (x2 > Surface->w - 1)
1038 x2 = Surface->w - 1;
1045 SDL_FillRect(Surface, &l, Color);
1048 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1053 if (SDL_MUSTLOCK(Surface))
1055 if (SDL_LockSurface(Surface) < 0)
1068 /* Do the clipping */
1069 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1073 if (y2 > Surface->h - 1)
1074 y2 = Surface->h - 1;
1081 SDL_FillRect(Surface, &l, Color);
1083 if (SDL_MUSTLOCK(Surface))
1085 SDL_UnlockSurface(Surface);
1089 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1090 Uint8 R, Uint8 G, Uint8 B)
1092 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1095 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
1106 /* Do the clipping */
1107 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1111 if (y2 > Surface->h - 1)
1112 y2 = Surface->h - 1;
1119 SDL_FillRect(Surface, &l, Color);
1122 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1123 Sint16 x2, Sint16 y2, Uint32 Color,
1124 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1127 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1132 sdx = (dx < 0) ? -1 : 1;
1133 sdy = (dy < 0) ? -1 : 1;
1145 for (x = 0; x < dx; x++)
1147 Callback(Surface, px, py, Color);
1161 for (y = 0; y < dy; y++)
1163 Callback(Surface, px, py, Color);
1177 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1178 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1179 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1182 sge_DoLine(Surface, X1, Y1, X2, Y2,
1183 SDL_MapRGB(Surface->format, R, G, B), Callback);
1186 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1189 if (SDL_MUSTLOCK(Surface))
1191 if (SDL_LockSurface(Surface) < 0)
1196 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1198 /* unlock the display */
1199 if (SDL_MUSTLOCK(Surface))
1201 SDL_UnlockSurface(Surface);
1205 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1206 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1208 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1211 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1213 if (dst_bitmap == backbuffer || dst_bitmap == window)
1219 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1224 -----------------------------------------------------------------------------
1225 quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1226 -----------------------------------------------------------------------------
1229 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1230 int width, int height, Uint32 color)
1234 for (y = src_y; y < src_y + height; y++)
1236 for (x = src_x; x < src_x + width; x++)
1238 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1240 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1245 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1246 int src_x, int src_y, int width, int height,
1247 int dst_x, int dst_y)
1251 for (y = 0; y < height; y++)
1253 for (x = 0; x < width; x++)
1255 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1257 if (pixel != BLACK_PIXEL)
1258 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1264 /* ========================================================================= */
1265 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
1266 /* (Rotozoomer) by Andreas Schiffler */
1267 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html */
1268 /* ========================================================================= */
1271 -----------------------------------------------------------------------------
1274 zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1275 -----------------------------------------------------------------------------
1286 int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1289 tColorRGBA *sp, *csp, *dp;
1293 sp = csp = (tColorRGBA *) src->pixels;
1294 dp = (tColorRGBA *) dst->pixels;
1295 sgap = src->pitch - src->w * 4;
1296 dgap = dst->pitch - dst->w * 4;
1298 for (y = 0; y < dst->h; y++)
1302 for (x = 0; x < dst->w; x++)
1304 tColorRGBA *sp0 = sp;
1305 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1306 tColorRGBA *sp00 = &sp0[0];
1307 tColorRGBA *sp01 = &sp0[1];
1308 tColorRGBA *sp10 = &sp1[0];
1309 tColorRGBA *sp11 = &sp1[1];
1312 /* create new color pixel from all four source color pixels */
1313 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1314 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1315 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1316 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1321 /* advance source pointers */
1324 /* advance destination pointer */
1328 /* advance source pointer */
1329 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1331 /* advance destination pointers */
1332 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1338 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1340 int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1341 tColorRGBA *sp, *csp, *dp;
1344 /* use specialized zoom function when scaling down to exactly half size */
1345 if (src->w == 2 * dst->w &&
1346 src->h == 2 * dst->h)
1347 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1349 /* variable setup */
1350 sx = (int) (65536.0 * (float) src->w / (float) dst->w);
1351 sy = (int) (65536.0 * (float) src->h / (float) dst->h);
1353 /* allocate memory for row increments */
1354 sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1355 say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1357 /* precalculate row increments */
1360 for (x = 0; x <= dst->w; x++)
1370 for (y = 0; y <= dst->h; y++)
1379 sp = csp = (tColorRGBA *) src->pixels;
1380 dp = (tColorRGBA *) dst->pixels;
1381 sgap = src->pitch - src->w * 4;
1382 dgap = dst->pitch - dst->w * 4;
1385 for (y = 0; y < dst->h; y++)
1390 for (x = 0; x < dst->w; x++)
1395 /* advance source pointers */
1397 sp += (*csax >> 16);
1399 /* advance destination pointer */
1403 /* advance source pointer */
1405 csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
1407 /* advance destination pointers */
1408 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1418 -----------------------------------------------------------------------------
1421 zoomes 8 bit palette/Y 'src' surface to 'dst' surface
1422 -----------------------------------------------------------------------------
1425 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
1427 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1428 Uint8 *sp, *dp, *csp;
1431 /* variable setup */
1432 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
1433 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
1435 /* allocate memory for row increments */
1436 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
1437 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
1439 /* precalculate row increments */
1442 for (x = 0; x < dst->w; x++)
1445 *csax = (csx >> 16);
1452 for (y = 0; y < dst->h; y++)
1455 *csay = (csy >> 16);
1462 for (x = 0; x < dst->w; x++)
1470 for (y = 0; y < dst->h; y++)
1477 sp = csp = (Uint8 *) src->pixels;
1478 dp = (Uint8 *) dst->pixels;
1479 dgap = dst->pitch - dst->w;
1483 for (y = 0; y < dst->h; y++)
1487 for (x = 0; x < dst->w; x++)
1492 /* advance source pointers */
1496 /* advance destination pointer */
1500 /* advance source pointer (for row) */
1501 csp += ((*csay) * src->pitch);
1504 /* advance destination pointers */
1515 -----------------------------------------------------------------------------
1518 Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
1519 'zoomx' and 'zoomy' are scaling factors for width and height.
1520 If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
1521 into a 32bit RGBA format on the fly.
1522 -----------------------------------------------------------------------------
1525 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
1527 SDL_Surface *zoom_src = NULL;
1528 SDL_Surface *zoom_dst = NULL;
1529 boolean is_converted = FALSE;
1536 /* determine if source surface is 32 bit or 8 bit */
1537 is_32bit = (src->format->BitsPerPixel == 32);
1539 if (is_32bit || src->format->BitsPerPixel == 8)
1541 /* use source surface 'as is' */
1546 /* new source surface is 32 bit with a defined RGB ordering */
1547 zoom_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1548 0x000000ff, 0x0000ff00, 0x00ff0000, 0);
1549 SDL_BlitSurface(src, NULL, zoom_src, NULL);
1551 is_converted = TRUE;
1554 /* allocate surface to completely contain the zoomed surface */
1557 /* target surface is 32 bit with source RGBA/ABGR ordering */
1558 zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 32,
1559 zoom_src->format->Rmask,
1560 zoom_src->format->Gmask,
1561 zoom_src->format->Bmask, 0);
1565 /* target surface is 8 bit */
1566 zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 8,
1570 /* lock source surface */
1571 SDL_LockSurface(zoom_src);
1573 /* check which kind of surface we have */
1576 /* call the 32 bit transformation routine to do the zooming */
1577 zoomSurfaceRGBA(zoom_src, zoom_dst);
1582 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
1583 zoom_dst->format->palette->colors[i] =
1584 zoom_src->format->palette->colors[i];
1585 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
1587 /* call the 8 bit transformation routine to do the zooming */
1588 zoomSurfaceY(zoom_src, zoom_dst);
1591 /* unlock source surface */
1592 SDL_UnlockSurface(zoom_src);
1594 /* free temporary surface */
1596 SDL_FreeSurface(zoom_src);
1598 /* return destination surface */
1602 void SDLZoomBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap)
1604 SDL_Surface *sdl_surface_tmp;
1605 int dst_width = dst_bitmap->width;
1606 int dst_height = dst_bitmap->height;
1608 /* throw away old destination surface */
1609 SDL_FreeSurface(dst_bitmap->surface);
1611 /* create zoomed temporary surface from source surface */
1612 sdl_surface_tmp = zoomSurface(src_bitmap->surface, dst_width, dst_height);
1614 /* create native format destination surface from zoomed temporary surface */
1615 dst_bitmap->surface = SDL_DisplayFormat(sdl_surface_tmp);
1617 /* free temporary surface */
1618 SDL_FreeSurface(sdl_surface_tmp);
1622 /* ========================================================================= */
1623 /* load image to bitmap */
1624 /* ========================================================================= */
1626 Bitmap *SDLLoadImage(char *filename)
1628 Bitmap *new_bitmap = CreateBitmapStruct();
1629 SDL_Surface *sdl_image_tmp;
1631 /* load image to temporary surface */
1632 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
1634 SetError("IMG_Load(): %s", SDL_GetError());
1639 UPDATE_BUSY_STATE();
1641 /* create native non-transparent surface for current image */
1642 if ((new_bitmap->surface = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1644 SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1649 UPDATE_BUSY_STATE();
1651 /* create native transparent surface for current image */
1652 SDL_SetColorKey(sdl_image_tmp, SDL_SRCCOLORKEY,
1653 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
1654 if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1656 SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1661 UPDATE_BUSY_STATE();
1663 /* free temporary surface */
1664 SDL_FreeSurface(sdl_image_tmp);
1666 new_bitmap->width = new_bitmap->surface->w;
1667 new_bitmap->height = new_bitmap->surface->h;
1673 /* ------------------------------------------------------------------------- */
1674 /* custom cursor fuctions */
1675 /* ------------------------------------------------------------------------- */
1677 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
1679 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
1680 cursor_info->width, cursor_info->height,
1681 cursor_info->hot_x, cursor_info->hot_y);
1684 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
1686 static struct MouseCursorInfo *last_cursor_info = NULL;
1687 static struct MouseCursorInfo *last_cursor_info2 = NULL;
1688 static SDL_Cursor *cursor_default = NULL;
1689 static SDL_Cursor *cursor_current = NULL;
1691 /* if invoked for the first time, store the SDL default cursor */
1692 if (cursor_default == NULL)
1693 cursor_default = SDL_GetCursor();
1695 /* only create new cursor if cursor info (custom only) has changed */
1696 if (cursor_info != NULL && cursor_info != last_cursor_info)
1698 cursor_current = create_cursor(cursor_info);
1699 last_cursor_info = cursor_info;
1702 /* only set new cursor if cursor info (custom or NULL) has changed */
1703 if (cursor_info != last_cursor_info2)
1704 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
1706 last_cursor_info2 = cursor_info;
1710 /* ========================================================================= */
1711 /* audio functions */
1712 /* ========================================================================= */
1714 void SDLOpenAudio(void)
1716 if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
1717 SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
1719 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
1721 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
1725 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
1726 AUDIO_NUM_CHANNELS_STEREO,
1727 setup.system.audio_fragment_size) < 0)
1729 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
1733 audio.sound_available = TRUE;
1734 audio.music_available = TRUE;
1735 audio.loops_available = TRUE;
1736 audio.sound_enabled = TRUE;
1738 /* set number of available mixer channels */
1739 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
1740 audio.music_channel = MUSIC_CHANNEL;
1741 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
1743 Mixer_InitChannels();
1746 void SDLCloseAudio(void)
1749 Mix_HaltChannel(-1);
1752 SDL_QuitSubSystem(SDL_INIT_AUDIO);
1756 /* ========================================================================= */
1757 /* event functions */
1758 /* ========================================================================= */
1760 void SDLNextEvent(Event *event)
1762 SDL_WaitEvent(event);
1764 if (event->type == EVENT_BUTTONPRESS ||
1765 event->type == EVENT_BUTTONRELEASE)
1767 if (((ButtonEvent *)event)->x > video_xoffset)
1768 ((ButtonEvent *)event)->x -= video_xoffset;
1770 ((ButtonEvent *)event)->x = 0;
1771 if (((ButtonEvent *)event)->y > video_yoffset)
1772 ((ButtonEvent *)event)->y -= video_yoffset;
1774 ((ButtonEvent *)event)->y = 0;
1776 else if (event->type == EVENT_MOTIONNOTIFY)
1778 if (((MotionEvent *)event)->x > video_xoffset)
1779 ((MotionEvent *)event)->x -= video_xoffset;
1781 ((MotionEvent *)event)->x = 0;
1782 if (((MotionEvent *)event)->y > video_yoffset)
1783 ((MotionEvent *)event)->y -= video_yoffset;
1785 ((MotionEvent *)event)->y = 0;
1790 /* ========================================================================= */
1791 /* joystick functions */
1792 /* ========================================================================= */
1794 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
1795 static int sdl_js_axis[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1796 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1798 static boolean SDLOpenJoystick(int nr)
1800 if (nr < 0 || nr > MAX_PLAYERS)
1803 return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
1806 static void SDLCloseJoystick(int nr)
1808 if (nr < 0 || nr > MAX_PLAYERS)
1811 SDL_JoystickClose(sdl_joystick[nr]);
1814 static boolean SDLCheckJoystickOpened(int nr)
1816 if (nr < 0 || nr > MAX_PLAYERS)
1819 return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
1822 void HandleJoystickEvent(Event *event)
1826 case SDL_JOYAXISMOTION:
1827 if (event->jaxis.axis < 2)
1828 sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
1831 case SDL_JOYBUTTONDOWN:
1832 if (event->jbutton.button < 2)
1833 sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
1836 case SDL_JOYBUTTONUP:
1837 if (event->jbutton.button < 2)
1838 sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
1846 void SDLInitJoysticks()
1848 static boolean sdl_joystick_subsystem_initialized = FALSE;
1849 boolean print_warning = !sdl_joystick_subsystem_initialized;
1852 if (!sdl_joystick_subsystem_initialized)
1854 sdl_joystick_subsystem_initialized = TRUE;
1856 if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
1858 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
1863 for (i = 0; i < MAX_PLAYERS; i++)
1865 /* get configured joystick for this player */
1866 char *device_name = setup.input[i].joy.device_name;
1867 int joystick_nr = getJoystickNrFromDeviceName(device_name);
1869 if (joystick_nr >= SDL_NumJoysticks())
1871 if (setup.input[i].use_joystick && print_warning)
1872 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
1877 /* misuse joystick file descriptor variable to store joystick number */
1878 joystick.fd[i] = joystick_nr;
1880 if (joystick_nr == -1)
1883 /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
1884 if (SDLCheckJoystickOpened(joystick_nr))
1885 SDLCloseJoystick(joystick_nr);
1887 if (!setup.input[i].use_joystick)
1890 if (!SDLOpenJoystick(joystick_nr))
1893 Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
1898 joystick.status = JOYSTICK_ACTIVATED;
1902 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1904 if (nr < 0 || nr >= MAX_PLAYERS)
1908 *x = sdl_js_axis[nr][0];
1910 *y = sdl_js_axis[nr][1];
1913 *b1 = sdl_js_button[nr][0];
1915 *b2 = sdl_js_button[nr][1];
1920 #endif /* TARGET_SDL */