f751fe85be2a5e568b94c6929f3ea79fc5f3d0ee
[rocksndiamonds.git] / src / libgame / sdl.c
1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1994-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * sdl.c                                                    *
12 ***********************************************************/
13
14 #include "system.h"
15 #include "sound.h"
16 #include "joystick.h"
17 #include "misc.h"
18 #include "setup.h"
19
20
21 #if defined(TARGET_SDL)
22
23 /* ========================================================================= */
24 /* video functions                                                           */
25 /* ========================================================================= */
26
27 /* functions from SGE library */
28 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
29
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;
37
38 static void setFullscreenParameters(char *fullscreen_mode_string)
39 {
40   struct ScreenModeInfo *fullscreen_mode;
41   int i;
42
43   fullscreen_mode = get_screen_mode_from_string(fullscreen_mode_string);
44
45   if (fullscreen_mode == NULL)
46     return;
47
48   for (i = 0; video.fullscreen_modes[i].width != -1; i++)
49   {
50     if (fullscreen_mode->width  == video.fullscreen_modes[i].width &&
51         fullscreen_mode->height == video.fullscreen_modes[i].height)
52     {
53       fullscreen_width  = fullscreen_mode->width;
54       fullscreen_height = fullscreen_mode->height;
55
56       fullscreen_xoffset = (fullscreen_width  - video.width)  / 2;
57       fullscreen_yoffset = (fullscreen_height - video.height) / 2;
58
59       break;
60     }
61   }
62 }
63
64 static void SDLSetWindowIcon(char *basename)
65 {
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) */
68
69 #if !defined(PLATFORM_MACOSX)
70   char *filename = getCustomImageFilename(basename);
71   SDL_Surface *surface;
72
73   if (filename == NULL)
74   {
75     Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
76
77     return;
78   }
79
80   if ((surface = IMG_Load(filename)) == NULL)
81   {
82     Error(ERR_WARN, "IMG_Load() failed: %s", SDL_GetError());
83
84     return;
85   }
86
87   /* set transparent color */
88   SDL_SetColorKey(surface, SDL_SRCCOLORKEY,
89                   SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
90
91   SDL_WM_SetIcon(surface, NULL);
92 #endif
93 }
94
95 void SDLInitVideoDisplay(void)
96 {
97   if (!strEqual(setup.system.sdl_videodriver, ARG_DEFAULT))
98     SDL_putenv(getStringCat2("SDL_VIDEODRIVER=", setup.system.sdl_videodriver));
99
100   SDL_putenv("SDL_VIDEO_CENTERED=1");
101
102   /* initialize SDL video */
103   if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
104     Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
105
106   /* set default SDL depth */
107   video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
108 }
109
110 void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
111                         boolean fullscreen)
112 {
113   static int screen_xy[][2] =
114   {
115     {  640, 480 },
116     {  800, 600 },
117     { 1024, 768 },
118     {   -1,  -1 }
119   };
120   SDL_Rect **modes;
121   int i, j;
122
123   /* default: normal game window size */
124   fullscreen_width = video.width;
125   fullscreen_height = video.height;
126   fullscreen_xoffset = 0;
127   fullscreen_yoffset = 0;
128
129   for (i = 0; screen_xy[i][0] != -1; i++)
130   {
131     if (screen_xy[i][0] >= video.width && screen_xy[i][1] >= video.height)
132     {
133       fullscreen_width  = screen_xy[i][0];
134       fullscreen_height = screen_xy[i][1];
135
136       break;
137     }
138   }
139
140   fullscreen_xoffset = (fullscreen_width  - video.width)  / 2;
141   fullscreen_yoffset = (fullscreen_height - video.height) / 2;
142
143   /* get available hardware supported fullscreen modes */
144   modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_HWSURFACE);
145
146   if (modes == NULL)
147   {
148     /* no screen modes available => no fullscreen mode support */
149     video.fullscreen_available = FALSE;
150   }
151   else if (modes == (SDL_Rect **)-1)
152   {
153     /* fullscreen resolution is not restricted -- all resolutions available */
154     video.fullscreen_modes = checked_calloc(2 * sizeof(struct ScreenModeInfo));
155
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;
159
160     video.fullscreen_modes[1].width  = -1;
161     video.fullscreen_modes[1].height = -1;
162   }
163   else
164   {
165     /* in this case, a certain number of screen modes is available */
166     int num_modes = 0;
167
168     for(i = 0; modes[i] != NULL; i++)
169     {
170       boolean found_mode = FALSE;
171
172       /* screen mode is smaller than video buffer size -- skip it */
173       if (modes[i]->w < video.width || modes[i]->h < video.height)
174         continue;
175
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)
180             found_mode = TRUE;
181
182       if (found_mode)           /* screen mode already stored -- skip it */
183         continue;
184
185       /* new mode found; add it to list of available fullscreen modes */
186
187       num_modes++;
188
189       video.fullscreen_modes = checked_realloc(video.fullscreen_modes,
190                                                (num_modes + 1) *
191                                                sizeof(struct ScreenModeInfo));
192
193       video.fullscreen_modes[num_modes - 1].width  = modes[i]->w;
194       video.fullscreen_modes[num_modes - 1].height = modes[i]->h;
195
196       video.fullscreen_modes[num_modes].width  = -1;
197       video.fullscreen_modes[num_modes].height = -1;
198     }
199
200     if (num_modes == 0)
201     {
202       /* no appropriate screen modes available => no fullscreen mode support */
203       video.fullscreen_available = FALSE;
204     }
205   }
206
207   /* set window icon */
208   SDLSetWindowIcon(program.sdl_icon_filename);
209
210   /* open SDL video output device (window or fullscreen mode) */
211   if (!SDLSetVideoMode(backbuffer, fullscreen))
212     Error(ERR_EXIT, "setting video mode failed");
213
214   /* set window and icon title */
215   SDL_WM_SetCaption(program.window_title, program.window_title);
216
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.
225
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. */
229
230   /* create additional (symbolic) buffer for double-buffering */
231   *window = CreateBitmap(video.width, video.height, video.depth);
232 }
233
234 boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
235 {
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;
240
241   if (*backbuffer == NULL)
242     *backbuffer = CreateBitmapStruct();
243
244   if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
245   {
246     setFullscreenParameters(setup.fullscreen_mode);
247
248     video_xoffset = fullscreen_xoffset;
249     video_yoffset = fullscreen_yoffset;
250
251     /* switch display to fullscreen mode, if available */
252     if ((new_surface = SDL_SetVideoMode(fullscreen_width, fullscreen_height,
253                                         video.depth, surface_flags_fullscreen))
254         == NULL)
255     {
256       /* switching display to fullscreen mode failed */
257       Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
258
259       /* do not try it again */
260       video.fullscreen_available = FALSE;
261
262       success = FALSE;
263     }
264     else
265     {
266       (*backbuffer)->surface = new_surface;
267
268       video.fullscreen_enabled = TRUE;
269       video.fullscreen_mode_current = setup.fullscreen_mode;
270
271       success = TRUE;
272     }
273   }
274
275   if ((!fullscreen && video.fullscreen_enabled) || new_surface == NULL)
276   {
277     video_xoffset = 0;
278     video_yoffset = 0;
279
280     /* switch display to window mode */
281     if ((new_surface = SDL_SetVideoMode(video.width, video.height,
282                                         video.depth, surface_flags_window))
283         == NULL)
284     {
285       /* switching display to window mode failed -- should not happen */
286       Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
287
288       success = FALSE;
289     }
290     else
291     {
292       (*backbuffer)->surface = new_surface;
293
294       video.fullscreen_enabled = FALSE;
295       success = TRUE;
296     }
297   }
298
299
300 #if 1
301   SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
302
303 #if defined(PLATFORM_WIN32)
304   {
305     SDL_SysWMinfo wminfo;
306     HWND hwnd;
307
308     SDL_VERSION(&wminfo.version);
309     SDL_GetWMInfo(&wminfo);
310
311     hwnd = wminfo.window;
312
313     DragAcceptFiles(hwnd, TRUE);
314   }
315 #endif
316 #endif
317
318
319   return success;
320 }
321
322 void SDLCreateBitmapContent(Bitmap *new_bitmap, int width, int height,
323                             int depth)
324 {
325   SDL_Surface *surface_tmp, *surface_native;
326
327   if ((surface_tmp = SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth,
328                                           0, 0, 0, 0))
329       == NULL)
330     Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
331
332   if ((surface_native = SDL_DisplayFormat(surface_tmp)) == NULL)
333     Error(ERR_EXIT, "SDL_DisplayFormat() failed: %s", SDL_GetError());
334
335   SDL_FreeSurface(surface_tmp);
336
337   new_bitmap->surface = surface_native;
338 }
339
340 void SDLFreeBitmapPointers(Bitmap *bitmap)
341 {
342   if (bitmap->surface)
343     SDL_FreeSurface(bitmap->surface);
344   if (bitmap->surface_masked)
345     SDL_FreeSurface(bitmap->surface_masked);
346   bitmap->surface = NULL;
347   bitmap->surface_masked = NULL;
348 }
349
350 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
351                  int src_x, int src_y, int width, int height,
352                  int dst_x, int dst_y, int mask_mode)
353 {
354   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
355   SDL_Rect src_rect, dst_rect;
356
357   if (src_bitmap == backbuffer)
358   {
359     src_x += video_xoffset;
360     src_y += video_yoffset;
361   }
362
363   src_rect.x = src_x;
364   src_rect.y = src_y;
365   src_rect.w = width;
366   src_rect.h = height;
367
368   if (dst_bitmap == backbuffer || dst_bitmap == window)
369   {
370     dst_x += video_xoffset;
371     dst_y += video_yoffset;
372   }
373
374   dst_rect.x = dst_x;
375   dst_rect.y = dst_y;
376   dst_rect.w = width;
377   dst_rect.h = height;
378
379   if (src_bitmap != backbuffer || dst_bitmap != window)
380     SDL_BlitSurface((mask_mode == BLIT_MASKED ?
381                      src_bitmap->surface_masked : src_bitmap->surface),
382                     &src_rect, real_dst_bitmap->surface, &dst_rect);
383
384   if (dst_bitmap == window)
385     SDL_UpdateRect(backbuffer->surface, dst_x, dst_y, width, height);
386 }
387
388 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
389                       Uint32 color)
390 {
391   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
392   SDL_Rect rect;
393
394   if (dst_bitmap == backbuffer || dst_bitmap == window)
395   {
396     x += video_xoffset;
397     y += video_yoffset;
398   }
399
400   rect.x = x;
401   rect.y = y;
402   rect.w = width;
403   rect.h = height;
404
405   SDL_FillRect(real_dst_bitmap->surface, &rect, color);
406
407   if (dst_bitmap == window)
408     SDL_UpdateRect(backbuffer->surface, x, y, width, height);
409 }
410
411 void SDLFadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
412                       int fade_mode, int fade_delay, int post_delay,
413                       void (*draw_border_function)(void))
414 {
415   static boolean initialization_needed = TRUE;
416   static SDL_Surface *surface_source = NULL;
417   static SDL_Surface *surface_target = NULL;
418   static SDL_Surface *surface_black = NULL;
419   SDL_Surface *surface_screen = backbuffer->surface;
420   SDL_Surface *surface_cross = (bitmap_cross ? bitmap_cross->surface : NULL);
421   SDL_Rect src_rect, dst_rect;
422   int src_x = x, src_y = y;
423   int dst_x = x, dst_y = y;
424   unsigned int time_last, time_current;
425
426   src_rect.x = src_x;
427   src_rect.y = src_y;
428   src_rect.w = width;
429   src_rect.h = height;
430
431   dst_x += video_xoffset;
432   dst_y += video_yoffset;
433
434   dst_rect.x = dst_x;
435   dst_rect.y = dst_y;
436   dst_rect.w = width;           /* (ignored) */
437   dst_rect.h = height;          /* (ignored) */
438
439   if (initialization_needed)
440   {
441     unsigned int flags = SDL_SRCALPHA;
442
443     /* use same surface type as screen surface */
444     if ((surface_screen->flags & SDL_HWSURFACE))
445       flags |= SDL_HWSURFACE;
446     else
447       flags |= SDL_SWSURFACE;
448
449     /* create surface for temporary copy of screen buffer (source) */
450     if ((surface_source =
451          SDL_CreateRGBSurface(flags,
452                               video.width,
453                               video.height,
454                               surface_screen->format->BitsPerPixel,
455                               surface_screen->format->Rmask,
456                               surface_screen->format->Gmask,
457                               surface_screen->format->Bmask,
458                               surface_screen->format->Amask)) == NULL)
459       Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
460
461     /* create surface for cross-fading screen buffer (target) */
462     if ((surface_target =
463          SDL_CreateRGBSurface(flags,
464                               video.width,
465                               video.height,
466                               surface_screen->format->BitsPerPixel,
467                               surface_screen->format->Rmask,
468                               surface_screen->format->Gmask,
469                               surface_screen->format->Bmask,
470                               surface_screen->format->Amask)) == NULL)
471       Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
472
473     /* create black surface for fading from/to black */
474     if ((surface_black =
475          SDL_CreateRGBSurface(flags,
476                               video.width,
477                               video.height,
478                               surface_screen->format->BitsPerPixel,
479                               surface_screen->format->Rmask,
480                               surface_screen->format->Gmask,
481                               surface_screen->format->Bmask,
482                               surface_screen->format->Amask)) == NULL)
483       Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
484
485     /* completely fill the surface with black color pixels */
486     SDL_FillRect(surface_black, NULL,
487                  SDL_MapRGB(surface_screen->format, 0, 0, 0));
488
489     initialization_needed = FALSE;
490   }
491
492   /* copy source and target surfaces to temporary surfaces for fading */
493   if (fade_mode & FADE_TYPE_TRANSFORM)
494   {
495     SDL_BlitSurface(surface_cross,  &src_rect, surface_source, &src_rect);
496     SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
497   }
498   else if (fade_mode & FADE_TYPE_FADE_IN)
499   {
500     SDL_BlitSurface(surface_black,  &src_rect, surface_source, &src_rect);
501     SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
502   }
503   else          /* FADE_TYPE_FADE_OUT */
504   {
505     SDL_BlitSurface(surface_screen, &dst_rect, surface_source, &src_rect);
506     SDL_BlitSurface(surface_black,  &src_rect, surface_target, &src_rect);
507   }
508
509   time_current = SDL_GetTicks();
510
511   if (fade_mode == FADE_MODE_MELT)
512   {
513     boolean done = FALSE;
514     int melt_pixels = 2;
515     int melt_columns = width / melt_pixels;
516     int ypos[melt_columns];
517     int max_steps = height / 8 + 32;
518     int steps_done = 0;
519     float steps = 0;
520     int i;
521
522     SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
523     SDL_SetAlpha(surface_target, 0, 0);         /* disable alpha blending */
524
525     ypos[0] = -GetSimpleRandom(16);
526
527     for (i = 1 ; i < melt_columns; i++)
528     {
529       int r = GetSimpleRandom(3) - 1;   /* randomly choose from { -1, 0, -1 } */
530
531       ypos[i] = ypos[i - 1] + r;
532
533       if (ypos[i] > 0)
534         ypos[i] = 0;
535       else
536         if (ypos[i] == -16)
537           ypos[i] = -15;
538     }
539
540     while (!done)
541     {
542       int steps_final;
543
544       time_last = time_current;
545       time_current = SDL_GetTicks();
546       steps += max_steps * ((float)(time_current - time_last) / fade_delay);
547       steps_final = MIN(MAX(0, steps), max_steps);
548
549       steps_done++;
550
551       done = (steps_done >= steps_final);
552
553       for (i = 0 ; i < melt_columns; i++)
554       {
555         if (ypos[i] < 0)
556         {
557           ypos[i]++;
558
559           done = FALSE;
560         }
561         else if (ypos[i] < height)
562         {
563           int y1 = 16;
564           int y2 = 8;
565           int y3 = 8;
566           int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
567
568           if (ypos[i] + dy >= height)
569             dy = height - ypos[i];
570
571           /* copy part of (appearing) target surface to upper area */
572           src_rect.x = src_x + i * melt_pixels;
573           // src_rect.y = src_y + ypos[i];
574           src_rect.y = src_y;
575           src_rect.w = melt_pixels;
576           // src_rect.h = dy;
577           src_rect.h = ypos[i] + dy;
578
579           dst_rect.x = dst_x + i * melt_pixels;
580           // dst_rect.y = dst_y + ypos[i];
581           dst_rect.y = dst_y;
582
583           if (steps_done >= steps_final)
584             SDL_BlitSurface(surface_target, &src_rect,
585                             surface_screen, &dst_rect);
586
587           ypos[i] += dy;
588
589           /* copy part of (disappearing) source surface to lower area */
590           src_rect.x = src_x + i * melt_pixels;
591           src_rect.y = src_y;
592           src_rect.w = melt_pixels;
593           src_rect.h = height - ypos[i];
594
595           dst_rect.x = dst_x + i * melt_pixels;
596           dst_rect.y = dst_y + ypos[i];
597
598           if (steps_done >= steps_final)
599             SDL_BlitSurface(surface_source, &src_rect,
600                             surface_screen, &dst_rect);
601
602           done = FALSE;
603         }
604         else
605         {
606           src_rect.x = src_x + i * melt_pixels;
607           src_rect.y = src_y;
608           src_rect.w = melt_pixels;
609           src_rect.h = height;
610
611           dst_rect.x = dst_x + i * melt_pixels;
612           dst_rect.y = dst_y;
613
614           if (steps_done >= steps_final)
615             SDL_BlitSurface(surface_target, &src_rect,
616                             surface_screen, &dst_rect);
617         }
618       }
619
620       if (steps_done >= steps_final)
621       {
622         if (draw_border_function != NULL)
623           draw_border_function();
624
625         SDL_UpdateRect(surface_screen, dst_x, dst_y, width, height);
626       }
627     }
628   }
629   else
630   {
631     float alpha;
632     int alpha_final;
633
634     for (alpha = 0.0; alpha < 255.0;)
635     {
636       time_last = time_current;
637       time_current = SDL_GetTicks();
638       alpha += 255 * ((float)(time_current - time_last) / fade_delay);
639       alpha_final = MIN(MAX(0, alpha), 255);
640
641       /* draw existing (source) image to screen buffer */
642       SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
643
644       /* draw new (target) image to screen buffer using alpha blending */
645       SDL_SetAlpha(surface_target, SDL_SRCALPHA, alpha_final);
646       SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
647
648       if (draw_border_function != NULL)
649         draw_border_function();
650
651 #if 1
652       /* only update the region of the screen that is affected from fading */
653       SDL_UpdateRect(surface_screen, dst_x, dst_y, width, height);
654 #else
655       SDL_Flip(surface_screen);
656 #endif
657     }
658   }
659
660   Delay(post_delay);
661 }
662
663 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
664                        int to_x, int to_y, Uint32 color)
665 {
666   SDL_Surface *surface = dst_bitmap->surface;
667   SDL_Rect rect;
668
669   if (from_x > to_x)
670     swap_numbers(&from_x, &to_x);
671
672   if (from_y > to_y)
673     swap_numbers(&from_y, &to_y);
674
675   rect.x = from_x;
676   rect.y = from_y;
677   rect.w = (to_x - from_x + 1);
678   rect.h = (to_y - from_y + 1);
679
680   if (dst_bitmap == backbuffer || dst_bitmap == window)
681   {
682     rect.x += video_xoffset;
683     rect.y += video_yoffset;
684   }
685
686   SDL_FillRect(surface, &rect, color);
687 }
688
689 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
690                  int to_x, int to_y, Uint32 color)
691 {
692   if (dst_bitmap == backbuffer || dst_bitmap == window)
693   {
694     from_x += video_xoffset;
695     from_y += video_yoffset;
696     to_x += video_xoffset;
697     to_y += video_yoffset;
698   }
699
700   sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
701 }
702
703 #if 0
704 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
705                   int num_points, Uint32 color)
706 {
707   int i, x, y;
708   int line_width = 4;
709
710   for (i = 0; i < num_points - 1; i++)
711   {
712     for (x = 0; x < line_width; x++)
713     {
714       for (y = 0; y < line_width; y++)
715       {
716         int dx = x - line_width / 2;
717         int dy = y - line_width / 2;
718
719         if ((x == 0 && y == 0) ||
720             (x == 0 && y == line_width - 1) ||
721             (x == line_width - 1 && y == 0) ||
722             (x == line_width - 1 && y == line_width - 1))
723           continue;
724
725         sge_Line(surface, points[i].x + dx, points[i].y + dy,
726                  points[i+1].x + dx, points[i+1].y + dy, color);
727       }
728     }
729   }
730 }
731 #endif
732
733 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
734 {
735   SDL_Surface *surface = src_bitmap->surface;
736
737   if (src_bitmap == backbuffer || src_bitmap == window)
738   {
739     x += video_xoffset;
740     y += video_yoffset;
741   }
742
743   switch (surface->format->BytesPerPixel)
744   {
745     case 1:             /* assuming 8-bpp */
746     {
747       return *((Uint8 *)surface->pixels + y * surface->pitch + x);
748     }
749     break;
750
751     case 2:             /* probably 15-bpp or 16-bpp */
752     {
753       return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
754     }
755     break;
756
757   case 3:               /* slow 24-bpp mode; usually not used */
758     {
759       /* does this work? */
760       Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
761       Uint32 color = 0;
762       int shift;
763
764       shift = surface->format->Rshift;
765       color |= *(pix + shift / 8) >> shift;
766       shift = surface->format->Gshift;
767       color |= *(pix + shift / 8) >> shift;
768       shift = surface->format->Bshift;
769       color |= *(pix + shift / 8) >> shift;
770
771       return color;
772     }
773     break;
774
775   case 4:               /* probably 32-bpp */
776     {
777       return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
778     }
779     break;
780   }
781
782   return 0;
783 }
784
785
786 /* ========================================================================= */
787 /* The following functions were taken from the SGE library                   */
788 /* (SDL Graphics Extension Library) by Anders Lindström                      */
789 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html                        */
790 /* ========================================================================= */
791
792 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
793 {
794   if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
795   {
796     switch (surface->format->BytesPerPixel)
797     {
798       case 1:
799       {
800         /* Assuming 8-bpp */
801         *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
802       }
803       break;
804
805       case 2:
806       {
807         /* Probably 15-bpp or 16-bpp */
808         *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
809       }
810       break;
811
812       case 3:
813       {
814         /* Slow 24-bpp mode, usually not used */
815         Uint8 *pix;
816         int shift;
817
818         /* Gack - slow, but endian correct */
819         pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
820         shift = surface->format->Rshift;
821         *(pix+shift/8) = color>>shift;
822         shift = surface->format->Gshift;
823         *(pix+shift/8) = color>>shift;
824         shift = surface->format->Bshift;
825         *(pix+shift/8) = color>>shift;
826       }
827       break;
828
829       case 4:
830       {
831         /* Probably 32-bpp */
832         *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
833       }
834       break;
835     }
836   }
837 }
838
839 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
840                   Uint8 R, Uint8 G, Uint8 B)
841 {
842   _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
843 }
844
845 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
846 {
847   *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
848 }
849
850 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
851 {
852   *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
853 }
854
855 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
856 {
857   Uint8 *pix;
858   int shift;
859
860   /* Gack - slow, but endian correct */
861   pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
862   shift = surface->format->Rshift;
863   *(pix+shift/8) = color>>shift;
864   shift = surface->format->Gshift;
865   *(pix+shift/8) = color>>shift;
866   shift = surface->format->Bshift;
867   *(pix+shift/8) = color>>shift;
868 }
869
870 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
871 {
872   *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
873 }
874
875 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
876 {
877   switch (dest->format->BytesPerPixel)
878   {
879     case 1:
880       *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
881       break;
882
883     case 2:
884       *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
885       break;
886
887     case 3:
888       _PutPixel24(dest,x,y,color);
889       break;
890
891     case 4:
892       *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
893       break;
894   }
895 }
896
897 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
898 {
899   if (SDL_MUSTLOCK(surface))
900   {
901     if (SDL_LockSurface(surface) < 0)
902     {
903       return;
904     }
905   }
906
907   _PutPixel(surface, x, y, color);
908
909   if (SDL_MUSTLOCK(surface))
910   {
911     SDL_UnlockSurface(surface);
912   }
913 }
914
915 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
916                   Uint8 r, Uint8 g, Uint8 b)
917 {
918   sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
919 }
920
921 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
922 {
923   if (y >= 0 && y <= dest->h - 1)
924   {
925     switch (dest->format->BytesPerPixel)
926     {
927       case 1:
928         return y*dest->pitch;
929         break;
930
931       case 2:
932         return y*dest->pitch/2;
933         break;
934
935       case 3:
936         return y*dest->pitch;
937         break;
938
939       case 4:
940         return y*dest->pitch/4;
941         break;
942     }
943   }
944
945   return -1;
946 }
947
948 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
949 {
950   if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
951   {
952     switch (surface->format->BytesPerPixel)
953     {
954       case 1:
955       {
956         /* Assuming 8-bpp */
957         *((Uint8 *)surface->pixels + ypitch + x) = color;
958       }
959       break;
960
961       case 2:
962       {
963         /* Probably 15-bpp or 16-bpp */
964         *((Uint16 *)surface->pixels + ypitch + x) = color;
965       }
966       break;
967
968       case 3:
969       {
970         /* Slow 24-bpp mode, usually not used */
971         Uint8 *pix;
972         int shift;
973
974         /* Gack - slow, but endian correct */
975         pix = (Uint8 *)surface->pixels + ypitch + x*3;
976         shift = surface->format->Rshift;
977         *(pix+shift/8) = color>>shift;
978         shift = surface->format->Gshift;
979         *(pix+shift/8) = color>>shift;
980         shift = surface->format->Bshift;
981         *(pix+shift/8) = color>>shift;
982       }
983       break;
984
985       case 4:
986       {
987         /* Probably 32-bpp */
988         *((Uint32 *)surface->pixels + ypitch + x) = color;
989       }
990       break;
991     }
992   }
993 }
994
995 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
996                Uint32 Color)
997 {
998   SDL_Rect l;
999
1000   if (SDL_MUSTLOCK(Surface))
1001   {
1002     if (SDL_LockSurface(Surface) < 0)
1003     {
1004       return;
1005     }
1006   }
1007
1008   if (x1 > x2)
1009   {
1010     Sint16 tmp = x1;
1011     x1 = x2;
1012     x2 = tmp;
1013   }
1014
1015   /* Do the clipping */
1016   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1017     return;
1018   if (x1 < 0)
1019     x1 = 0;
1020   if (x2 > Surface->w - 1)
1021     x2 = Surface->w - 1;
1022
1023   l.x = x1;
1024   l.y = y;
1025   l.w = x2 - x1 + 1;
1026   l.h = 1;
1027
1028   SDL_FillRect(Surface, &l, Color);
1029
1030   if (SDL_MUSTLOCK(Surface))
1031   {
1032     SDL_UnlockSurface(Surface);
1033   }
1034 }
1035
1036 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1037                   Uint8 R, Uint8 G, Uint8 B)
1038 {
1039   sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1040 }
1041
1042 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
1043 {
1044   SDL_Rect l;
1045
1046   if (x1 > x2)
1047   {
1048     Sint16 tmp = x1;
1049     x1 = x2;
1050     x2 = tmp;
1051   }
1052
1053   /* Do the clipping */
1054   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1055     return;
1056   if (x1 < 0)
1057     x1 = 0;
1058   if (x2 > Surface->w - 1)
1059     x2 = Surface->w - 1;
1060
1061   l.x = x1;
1062   l.y = y;
1063   l.w = x2 - x1 + 1;
1064   l.h = 1;
1065
1066   SDL_FillRect(Surface, &l, Color);
1067 }
1068
1069 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1070                Uint32 Color)
1071 {
1072   SDL_Rect l;
1073
1074   if (SDL_MUSTLOCK(Surface))
1075   {
1076     if (SDL_LockSurface(Surface) < 0)
1077     {
1078       return;
1079     }
1080   }
1081
1082   if (y1 > y2)
1083   {
1084     Sint16 tmp = y1;
1085     y1 = y2;
1086     y2 = tmp;
1087   }
1088
1089   /* Do the clipping */
1090   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1091     return;
1092   if (y1 < 0)
1093     y1 = 0;
1094   if (y2 > Surface->h - 1)
1095     y2 = Surface->h - 1;
1096
1097   l.x = x;
1098   l.y = y1;
1099   l.w = 1;
1100   l.h = y2 - y1 + 1;
1101
1102   SDL_FillRect(Surface, &l, Color);
1103
1104   if (SDL_MUSTLOCK(Surface))
1105   {
1106     SDL_UnlockSurface(Surface);
1107   }
1108 }
1109
1110 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1111                   Uint8 R, Uint8 G, Uint8 B)
1112 {
1113   sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1114 }
1115
1116 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
1117 {
1118   SDL_Rect l;
1119
1120   if (y1 > y2)
1121   {
1122     Sint16 tmp = y1;
1123     y1 = y2;
1124     y2 = tmp;
1125   }
1126
1127   /* Do the clipping */
1128   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1129     return;
1130   if (y1 < 0)
1131     y1 = 0;
1132   if (y2 > Surface->h - 1)
1133     y2 = Surface->h - 1;
1134
1135   l.x = x;
1136   l.y = y1;
1137   l.w = 1;
1138   l.h = y2 - y1 + 1;
1139
1140   SDL_FillRect(Surface, &l, Color);
1141 }
1142
1143 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1144                 Sint16 x2, Sint16 y2, Uint32 Color,
1145                 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1146                               Uint32 Color))
1147 {
1148   Sint16 dx, dy, sdx, sdy, x, y, px, py;
1149
1150   dx = x2 - x1;
1151   dy = y2 - y1;
1152
1153   sdx = (dx < 0) ? -1 : 1;
1154   sdy = (dy < 0) ? -1 : 1;
1155
1156   dx = sdx * dx + 1;
1157   dy = sdy * dy + 1;
1158
1159   x = y = 0;
1160
1161   px = x1;
1162   py = y1;
1163
1164   if (dx >= dy)
1165   {
1166     for (x = 0; x < dx; x++)
1167     {
1168       Callback(Surface, px, py, Color);
1169
1170       y += dy;
1171       if (y >= dx)
1172       {
1173         y -= dx;
1174         py += sdy;
1175       }
1176
1177       px += sdx;
1178     }
1179   }
1180   else
1181   {
1182     for (y = 0; y < dy; y++)
1183     {
1184       Callback(Surface, px, py, Color);
1185
1186       x += dx;
1187       if (x >= dy)
1188       {
1189         x -= dy;
1190         px += sdx;
1191       }
1192
1193       py += sdy;
1194     }
1195   }
1196 }
1197
1198 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1199                    Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1200                    void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1201                                  Uint32 Color))
1202 {
1203   sge_DoLine(Surface, X1, Y1, X2, Y2,
1204              SDL_MapRGB(Surface->format, R, G, B), Callback);
1205 }
1206
1207 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1208               Uint32 Color)
1209 {
1210   if (SDL_MUSTLOCK(Surface))
1211   {
1212     if (SDL_LockSurface(Surface) < 0)
1213       return;
1214    }
1215
1216    /* Draw the line */
1217    sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1218
1219    /* unlock the display */
1220    if (SDL_MUSTLOCK(Surface))
1221    {
1222       SDL_UnlockSurface(Surface);
1223    }
1224 }
1225
1226 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1227                  Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1228 {
1229   sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1230 }
1231
1232 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1233 {
1234   if (dst_bitmap == backbuffer || dst_bitmap == window)
1235   {
1236     x += video_xoffset;
1237     y += video_yoffset;
1238   }
1239
1240   sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1241 }
1242
1243
1244 /*
1245   -----------------------------------------------------------------------------
1246   quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1247   -----------------------------------------------------------------------------
1248 */
1249
1250 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1251                    int width, int height, Uint32 color)
1252 {
1253   int x, y;
1254
1255   for (y = src_y; y < src_y + height; y++)
1256   {
1257     for (x = src_x; x < src_x + width; x++)
1258     {
1259       Uint32 pixel = SDLGetPixel(bitmap, x, y);
1260
1261       SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1262     }
1263   }
1264 }
1265
1266 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1267                           int src_x, int src_y, int width, int height,
1268                           int dst_x, int dst_y)
1269 {
1270   int x, y;
1271
1272   for (y = 0; y < height; y++)
1273   {
1274     for (x = 0; x < width; x++)
1275     {
1276       Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1277
1278       if (pixel != BLACK_PIXEL)
1279         SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1280     }
1281   }
1282 }
1283
1284
1285 /* ========================================================================= */
1286 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
1287 /* (Rotozoomer) by Andreas Schiffler                                         */
1288 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html                   */
1289 /* ========================================================================= */
1290
1291 /*
1292   -----------------------------------------------------------------------------
1293   32 bit zoomer
1294
1295   zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1296   -----------------------------------------------------------------------------
1297 */
1298
1299 typedef struct
1300 {
1301   Uint8 r;
1302   Uint8 g;
1303   Uint8 b;
1304   Uint8 a;
1305 } tColorRGBA;
1306
1307 int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1308 {
1309   int x, y;
1310   tColorRGBA *sp, *csp, *dp;
1311   int sgap, dgap;
1312
1313   /* pointer setup */
1314   sp = csp = (tColorRGBA *) src->pixels;
1315   dp = (tColorRGBA *) dst->pixels;
1316   sgap = src->pitch - src->w * 4;
1317   dgap = dst->pitch - dst->w * 4;
1318
1319   for (y = 0; y < dst->h; y++)
1320   {
1321     sp = csp;
1322
1323     for (x = 0; x < dst->w; x++)
1324     {
1325       tColorRGBA *sp0 = sp;
1326       tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1327       tColorRGBA *sp00 = &sp0[0];
1328       tColorRGBA *sp01 = &sp0[1];
1329       tColorRGBA *sp10 = &sp1[0];
1330       tColorRGBA *sp11 = &sp1[1];
1331       tColorRGBA new;
1332
1333       /* create new color pixel from all four source color pixels */
1334       new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1335       new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1336       new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1337       new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1338
1339       /* draw */
1340       *dp = new;
1341
1342       /* advance source pointers */
1343       sp += 2;
1344
1345       /* advance destination pointer */
1346       dp++;
1347     }
1348
1349     /* advance source pointer */
1350     csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1351
1352     /* advance destination pointers */
1353     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1354   }
1355
1356   return 0;
1357 }
1358
1359 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1360 {
1361   int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1362   tColorRGBA *sp, *csp, *dp;
1363   int sgap, dgap;
1364
1365   /* use specialized zoom function when scaling down to exactly half size */
1366   if (src->w == 2 * dst->w &&
1367       src->h == 2 * dst->h)
1368     return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1369
1370   /* variable setup */
1371   sx = (int) (65536.0 * (float) src->w / (float) dst->w);
1372   sy = (int) (65536.0 * (float) src->h / (float) dst->h);
1373
1374   /* allocate memory for row increments */
1375   sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1376   say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1377
1378   /* precalculate row increments */
1379   csx = 0;
1380   csax = sax;
1381   for (x = 0; x <= dst->w; x++)
1382   {
1383     *csax = csx;
1384     csax++;
1385     csx &= 0xffff;
1386     csx += sx;
1387   }
1388
1389   csy = 0;
1390   csay = say;
1391   for (y = 0; y <= dst->h; y++)
1392   {
1393     *csay = csy;
1394     csay++;
1395     csy &= 0xffff;
1396     csy += sy;
1397   }
1398
1399   /* pointer setup */
1400   sp = csp = (tColorRGBA *) src->pixels;
1401   dp = (tColorRGBA *) dst->pixels;
1402   sgap = src->pitch - src->w * 4;
1403   dgap = dst->pitch - dst->w * 4;
1404
1405   csay = say;
1406   for (y = 0; y < dst->h; y++)
1407   {
1408     sp = csp;
1409     csax = sax;
1410
1411     for (x = 0; x < dst->w; x++)
1412     {
1413       /* draw */
1414       *dp = *sp;
1415
1416       /* advance source pointers */
1417       csax++;
1418       sp += (*csax >> 16);
1419
1420       /* advance destination pointer */
1421       dp++;
1422     }
1423
1424     /* advance source pointer */
1425     csay++;
1426     csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
1427
1428     /* advance destination pointers */
1429     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1430   }
1431
1432   free(sax);
1433   free(say);
1434
1435   return 0;
1436 }
1437
1438 /*
1439   -----------------------------------------------------------------------------
1440   8 bit zoomer
1441
1442   zoomes 8 bit palette/Y 'src' surface to 'dst' surface
1443   -----------------------------------------------------------------------------
1444 */
1445
1446 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
1447 {
1448   Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1449   Uint8 *sp, *dp, *csp;
1450   int dgap;
1451
1452   /* variable setup */
1453   sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
1454   sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
1455
1456   /* allocate memory for row increments */
1457   sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
1458   say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
1459
1460   /* precalculate row increments */
1461   csx = 0;
1462   csax = sax;
1463   for (x = 0; x < dst->w; x++)
1464   {
1465     csx += sx;
1466     *csax = (csx >> 16);
1467     csx &= 0xffff;
1468     csax++;
1469   }
1470
1471   csy = 0;
1472   csay = say;
1473   for (y = 0; y < dst->h; y++)
1474   {
1475     csy += sy;
1476     *csay = (csy >> 16);
1477     csy &= 0xffff;
1478     csay++;
1479   }
1480
1481   csx = 0;
1482   csax = sax;
1483   for (x = 0; x < dst->w; x++)
1484   {
1485     csx += (*csax);
1486     csax++;
1487   }
1488
1489   csy = 0;
1490   csay = say;
1491   for (y = 0; y < dst->h; y++)
1492   {
1493     csy += (*csay);
1494     csay++;
1495   }
1496
1497   /* pointer setup */
1498   sp = csp = (Uint8 *) src->pixels;
1499   dp = (Uint8 *) dst->pixels;
1500   dgap = dst->pitch - dst->w;
1501
1502   /* draw */
1503   csay = say;
1504   for (y = 0; y < dst->h; y++)
1505   {
1506     csax = sax;
1507     sp = csp;
1508     for (x = 0; x < dst->w; x++)
1509     {
1510       /* draw */
1511       *dp = *sp;
1512
1513       /* advance source pointers */
1514       sp += (*csax);
1515       csax++;
1516
1517       /* advance destination pointer */
1518       dp++;
1519     }
1520
1521     /* advance source pointer (for row) */
1522     csp += ((*csay) * src->pitch);
1523     csay++;
1524
1525     /* advance destination pointers */
1526     dp += dgap;
1527   }
1528
1529   free(sax);
1530   free(say);
1531
1532   return 0;
1533 }
1534
1535 /*
1536   -----------------------------------------------------------------------------
1537   zoomSurface()
1538
1539   Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
1540   'zoomx' and 'zoomy' are scaling factors for width and height.
1541   If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
1542   into a 32bit RGBA format on the fly.
1543   -----------------------------------------------------------------------------
1544 */
1545
1546 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
1547 {
1548   SDL_Surface *zoom_src = NULL;
1549   SDL_Surface *zoom_dst = NULL;
1550   boolean is_converted = FALSE;
1551   boolean is_32bit;
1552   int i;
1553
1554   if (src == NULL)
1555     return NULL;
1556
1557   /* determine if source surface is 32 bit or 8 bit */
1558   is_32bit = (src->format->BitsPerPixel == 32);
1559
1560   if (is_32bit || src->format->BitsPerPixel == 8)
1561   {
1562     /* use source surface 'as is' */
1563     zoom_src = src;
1564   }
1565   else
1566   {
1567     /* new source surface is 32 bit with a defined RGB ordering */
1568     zoom_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1569                                     0x000000ff, 0x0000ff00, 0x00ff0000, 0);
1570     SDL_BlitSurface(src, NULL, zoom_src, NULL);
1571     is_32bit = TRUE;
1572     is_converted = TRUE;
1573   }
1574
1575   /* allocate surface to completely contain the zoomed surface */
1576   if (is_32bit)
1577   {
1578     /* target surface is 32 bit with source RGBA/ABGR ordering */
1579     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 32,
1580                                     zoom_src->format->Rmask,
1581                                     zoom_src->format->Gmask,
1582                                     zoom_src->format->Bmask, 0);
1583   }
1584   else
1585   {
1586     /* target surface is 8 bit */
1587     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 8,
1588                                     0, 0, 0, 0);
1589   }
1590
1591   /* lock source surface */
1592   SDL_LockSurface(zoom_src);
1593
1594   /* check which kind of surface we have */
1595   if (is_32bit)
1596   {
1597     /* call the 32 bit transformation routine to do the zooming */
1598     zoomSurfaceRGBA(zoom_src, zoom_dst);
1599   }
1600   else
1601   {
1602     /* copy palette */
1603     for (i = 0; i < zoom_src->format->palette->ncolors; i++)
1604       zoom_dst->format->palette->colors[i] =
1605         zoom_src->format->palette->colors[i];
1606     zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
1607
1608     /* call the 8 bit transformation routine to do the zooming */
1609     zoomSurfaceY(zoom_src, zoom_dst);
1610   }
1611
1612   /* unlock source surface */
1613   SDL_UnlockSurface(zoom_src);
1614
1615   /* free temporary surface */
1616   if (is_converted)
1617     SDL_FreeSurface(zoom_src);
1618
1619   /* return destination surface */
1620   return zoom_dst;
1621 }
1622
1623 void SDLZoomBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap)
1624 {
1625   SDL_Surface *sdl_surface_tmp;
1626   int dst_width = dst_bitmap->width;
1627   int dst_height = dst_bitmap->height;
1628
1629   /* throw away old destination surface */
1630   SDL_FreeSurface(dst_bitmap->surface);
1631
1632   /* create zoomed temporary surface from source surface */
1633   sdl_surface_tmp = zoomSurface(src_bitmap->surface, dst_width, dst_height);
1634
1635   /* create native format destination surface from zoomed temporary surface */
1636   dst_bitmap->surface = SDL_DisplayFormat(sdl_surface_tmp);
1637
1638   /* free temporary surface */
1639   SDL_FreeSurface(sdl_surface_tmp);
1640 }
1641
1642
1643 /* ========================================================================= */
1644 /* load image to bitmap                                                      */
1645 /* ========================================================================= */
1646
1647 Bitmap *SDLLoadImage(char *filename)
1648 {
1649   Bitmap *new_bitmap = CreateBitmapStruct();
1650   SDL_Surface *sdl_image_tmp;
1651
1652   /* load image to temporary surface */
1653   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
1654   {
1655     SetError("IMG_Load(): %s", SDL_GetError());
1656
1657     return NULL;
1658   }
1659
1660   UPDATE_BUSY_STATE();
1661
1662   /* create native non-transparent surface for current image */
1663   if ((new_bitmap->surface = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1664   {
1665     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1666
1667     return NULL;
1668   }
1669
1670   UPDATE_BUSY_STATE();
1671
1672   /* create native transparent surface for current image */
1673   SDL_SetColorKey(sdl_image_tmp, SDL_SRCCOLORKEY,
1674                   SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
1675   if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1676   {
1677     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1678
1679     return NULL;
1680   }
1681
1682   UPDATE_BUSY_STATE();
1683
1684   /* free temporary surface */
1685   SDL_FreeSurface(sdl_image_tmp);
1686
1687   new_bitmap->width = new_bitmap->surface->w;
1688   new_bitmap->height = new_bitmap->surface->h;
1689
1690   return new_bitmap;
1691 }
1692
1693
1694 /* ------------------------------------------------------------------------- */
1695 /* custom cursor fuctions                                                    */
1696 /* ------------------------------------------------------------------------- */
1697
1698 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
1699 {
1700   return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
1701                           cursor_info->width, cursor_info->height,
1702                           cursor_info->hot_x, cursor_info->hot_y);
1703 }
1704
1705 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
1706 {
1707   static struct MouseCursorInfo *last_cursor_info = NULL;
1708   static struct MouseCursorInfo *last_cursor_info2 = NULL;
1709   static SDL_Cursor *cursor_default = NULL;
1710   static SDL_Cursor *cursor_current = NULL;
1711
1712   /* if invoked for the first time, store the SDL default cursor */
1713   if (cursor_default == NULL)
1714     cursor_default = SDL_GetCursor();
1715
1716   /* only create new cursor if cursor info (custom only) has changed */
1717   if (cursor_info != NULL && cursor_info != last_cursor_info)
1718   {
1719     cursor_current = create_cursor(cursor_info);
1720     last_cursor_info = cursor_info;
1721   }
1722
1723   /* only set new cursor if cursor info (custom or NULL) has changed */
1724   if (cursor_info != last_cursor_info2)
1725     SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
1726
1727   last_cursor_info2 = cursor_info;
1728 }
1729
1730
1731 /* ========================================================================= */
1732 /* audio functions                                                           */
1733 /* ========================================================================= */
1734
1735 void SDLOpenAudio(void)
1736 {
1737   if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
1738     SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
1739
1740   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
1741   {
1742     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
1743     return;
1744   }
1745
1746   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
1747                     AUDIO_NUM_CHANNELS_STEREO,
1748                     setup.system.audio_fragment_size) < 0)
1749   {
1750     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
1751     return;
1752   }
1753
1754   audio.sound_available = TRUE;
1755   audio.music_available = TRUE;
1756   audio.loops_available = TRUE;
1757   audio.sound_enabled = TRUE;
1758
1759   /* set number of available mixer channels */
1760   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
1761   audio.music_channel = MUSIC_CHANNEL;
1762   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
1763
1764   Mixer_InitChannels();
1765 }
1766
1767 void SDLCloseAudio(void)
1768 {
1769   Mix_HaltMusic();
1770   Mix_HaltChannel(-1);
1771
1772   Mix_CloseAudio();
1773   SDL_QuitSubSystem(SDL_INIT_AUDIO);
1774 }
1775
1776
1777 /* ========================================================================= */
1778 /* event functions                                                           */
1779 /* ========================================================================= */
1780
1781 void SDLNextEvent(Event *event)
1782 {
1783   SDL_WaitEvent(event);
1784
1785   if (event->type == EVENT_BUTTONPRESS ||
1786       event->type == EVENT_BUTTONRELEASE)
1787   {
1788     if (((ButtonEvent *)event)->x > video_xoffset)
1789       ((ButtonEvent *)event)->x -= video_xoffset;
1790     else
1791       ((ButtonEvent *)event)->x = 0;
1792     if (((ButtonEvent *)event)->y > video_yoffset)
1793       ((ButtonEvent *)event)->y -= video_yoffset;
1794     else
1795       ((ButtonEvent *)event)->y = 0;
1796   }
1797   else if (event->type == EVENT_MOTIONNOTIFY)
1798   {
1799     if (((MotionEvent *)event)->x > video_xoffset)
1800       ((MotionEvent *)event)->x -= video_xoffset;
1801     else
1802       ((MotionEvent *)event)->x = 0;
1803     if (((MotionEvent *)event)->y > video_yoffset)
1804       ((MotionEvent *)event)->y -= video_yoffset;
1805     else
1806       ((MotionEvent *)event)->y = 0;
1807   }
1808 }
1809
1810 void SDLHandleWindowManagerEvent(Event *event)
1811 {
1812 #if defined(PLATFORM_WIN32)
1813   SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
1814   SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
1815
1816   if (syswmmsg->msg == WM_DROPFILES)
1817   {
1818     HDROP hdrop = (HDROP)syswmmsg->wParam;
1819     int i, num_files;
1820
1821     printf("::: SDL_SYSWMEVENT:\n");
1822
1823     num_files = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
1824
1825     for (i = 0; i < num_files; i++)
1826     {
1827       int buffer_len = DragQueryFile(hdrop, i, NULL, 0);
1828       char buffer[buffer_len + 1];
1829
1830       DragQueryFile(hdrop, i, buffer, buffer_len + 1);
1831
1832       printf("::: - '%s'\n", buffer);
1833     }
1834
1835     DragFinish((HDROP)syswmmsg->wParam);
1836   }
1837 #endif
1838 }
1839
1840
1841 /* ========================================================================= */
1842 /* joystick functions                                                        */
1843 /* ========================================================================= */
1844
1845 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
1846 static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1847 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1848
1849 static boolean SDLOpenJoystick(int nr)
1850 {
1851   if (nr < 0 || nr > MAX_PLAYERS)
1852     return FALSE;
1853
1854   return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
1855 }
1856
1857 static void SDLCloseJoystick(int nr)
1858 {
1859   if (nr < 0 || nr > MAX_PLAYERS)
1860     return;
1861
1862   SDL_JoystickClose(sdl_joystick[nr]);
1863 }
1864
1865 static boolean SDLCheckJoystickOpened(int nr)
1866 {
1867   if (nr < 0 || nr > MAX_PLAYERS)
1868     return FALSE;
1869
1870   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
1871 }
1872
1873 void HandleJoystickEvent(Event *event)
1874 {
1875   switch(event->type)
1876   {
1877     case SDL_JOYAXISMOTION:
1878       if (event->jaxis.axis < 2)
1879         sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
1880       break;
1881
1882     case SDL_JOYBUTTONDOWN:
1883       if (event->jbutton.button < 2)
1884         sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
1885       break;
1886
1887     case SDL_JOYBUTTONUP:
1888       if (event->jbutton.button < 2)
1889         sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
1890       break;
1891
1892     default:
1893       break;
1894   }
1895 }
1896
1897 void SDLInitJoysticks()
1898 {
1899   static boolean sdl_joystick_subsystem_initialized = FALSE;
1900   boolean print_warning = !sdl_joystick_subsystem_initialized;
1901   int i;
1902
1903   if (!sdl_joystick_subsystem_initialized)
1904   {
1905     sdl_joystick_subsystem_initialized = TRUE;
1906
1907     if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
1908     {
1909       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
1910       return;
1911     }
1912   }
1913
1914   for (i = 0; i < MAX_PLAYERS; i++)
1915   {
1916     /* get configured joystick for this player */
1917     char *device_name = setup.input[i].joy.device_name;
1918     int joystick_nr = getJoystickNrFromDeviceName(device_name);
1919
1920     if (joystick_nr >= SDL_NumJoysticks())
1921     {
1922       if (setup.input[i].use_joystick && print_warning)
1923         Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
1924
1925       joystick_nr = -1;
1926     }
1927
1928     /* misuse joystick file descriptor variable to store joystick number */
1929     joystick.fd[i] = joystick_nr;
1930
1931     if (joystick_nr == -1)
1932       continue;
1933
1934     /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
1935     if (SDLCheckJoystickOpened(joystick_nr))
1936       SDLCloseJoystick(joystick_nr);
1937
1938     if (!setup.input[i].use_joystick)
1939       continue;
1940
1941     if (!SDLOpenJoystick(joystick_nr))
1942     {
1943       if (print_warning)
1944         Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
1945
1946       continue;
1947     }
1948
1949     joystick.status = JOYSTICK_ACTIVATED;
1950   }
1951 }
1952
1953 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1954 {
1955   if (nr < 0 || nr >= MAX_PLAYERS)
1956     return FALSE;
1957
1958   if (x != NULL)
1959     *x = sdl_js_axis[nr][0];
1960   if (y != NULL)
1961     *y = sdl_js_axis[nr][1];
1962
1963   if (b1 != NULL)
1964     *b1 = sdl_js_button[nr][0];
1965   if (b2 != NULL)
1966     *b2 = sdl_js_button[nr][1];
1967
1968   return TRUE;
1969 }
1970
1971 #endif /* TARGET_SDL */