rnd-20060804-1-src
[rocksndiamonds.git] / src / libgame / system.c
1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * system.c                                                 *
12 ***********************************************************/
13
14 #include <string.h>
15 #include <signal.h>
16
17 #include "platform.h"
18
19 #if defined(PLATFORM_MSDOS)
20 #include <fcntl.h>
21 #endif
22
23 #include "system.h"
24 #include "image.h"
25 #include "sound.h"
26 #include "setup.h"
27 #include "joystick.h"
28 #include "misc.h"
29
30
31 /* ========================================================================= */
32 /* exported variables                                                        */
33 /* ========================================================================= */
34
35 struct ProgramInfo      program;
36 struct OptionInfo       options;
37 struct VideoSystemInfo  video;
38 struct AudioSystemInfo  audio;
39 struct GfxInfo          gfx;
40 struct ArtworkInfo      artwork;
41 struct JoystickInfo     joystick;
42 struct SetupInfo        setup;
43
44 LevelDirTree           *leveldir_first_all = NULL;
45 LevelDirTree           *leveldir_first = NULL;
46 LevelDirTree           *leveldir_current = NULL;
47 int                     level_nr;
48
49 Display                *display = NULL;
50 Visual                 *visual = NULL;
51 int                     screen = 0;
52 Colormap                cmap = None;
53
54 DrawWindow             *window = NULL;
55 DrawBuffer             *backbuffer = NULL;
56 DrawBuffer             *drawto = NULL;
57
58 int                     button_status = MB_NOT_PRESSED;
59 boolean                 motion_status = FALSE;
60
61 int                     redraw_mask = REDRAW_NONE;
62 int                     redraw_tiles = 0;
63
64 int                     FrameCounter = 0;
65
66
67 /* ========================================================================= */
68 /* init/close functions                                                      */
69 /* ========================================================================= */
70
71 void InitProgramInfo(char *argv0,
72                      char *userdata_subdir, char *userdata_subdir_unix,
73                      char *program_title, char *window_title, char *icon_title,
74                      char *x11_icon_filename, char *x11_iconmask_filename,
75                      char *msdos_cursor_filename,
76                      char *cookie_prefix, char *filename_prefix,
77                      int program_version)
78 {
79   program.command_basepath = getBasePath(argv0);
80   program.command_basename = getBaseName(argv0);
81
82   program.userdata_subdir = userdata_subdir;
83   program.userdata_subdir_unix = userdata_subdir_unix;
84   program.userdata_path = getUserGameDataDir();
85
86   program.program_title = program_title;
87   program.window_title = window_title;
88   program.icon_title = icon_title;
89
90   program.x11_icon_filename = x11_icon_filename;
91   program.x11_iconmask_filename = x11_iconmask_filename;
92   program.msdos_cursor_filename = msdos_cursor_filename;
93
94   program.cookie_prefix = cookie_prefix;
95   program.filename_prefix = filename_prefix;
96
97   program.version_major = VERSION_MAJOR(program_version);
98   program.version_minor = VERSION_MINOR(program_version);
99   program.version_patch = VERSION_PATCH(program_version);
100
101   program.error_filename = getErrorFilename(ERROR_BASENAME);
102   program.error_file = stderr;
103 }
104
105 void InitExitFunction(void (*exit_function)(int))
106 {
107   program.exit_function = exit_function;
108
109   /* set signal handlers to custom exit function */
110   signal(SIGINT, exit_function);
111   signal(SIGTERM, exit_function);
112
113 #if defined(TARGET_SDL)
114   /* set exit function to automatically cleanup SDL stuff after exit() */
115   atexit(SDL_Quit);
116 #endif
117 }
118
119 void InitPlatformDependentStuff(void)
120 {
121 #if defined(PLATFORM_MSDOS)
122   _fmode = O_BINARY;
123 #endif
124
125 #if defined(PLATFORM_MACOSX)
126   updateUserGameDataDir();
127 #endif
128
129 #if !defined(PLATFORM_UNIX) || defined(PLATFORM_MACOSX)
130   openErrorFile();
131 #endif
132
133 #if defined(TARGET_SDL)
134   if (SDL_Init(SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE) < 0)
135     Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
136
137   SDLNet_Init();
138 #endif
139 }
140
141 void ClosePlatformDependentStuff(void)
142 {
143 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MSDOS)
144   closeErrorFile();
145 #endif
146
147 #if defined(PLATFORM_MSDOS)
148   dumpErrorFile();
149 #endif
150 }
151
152 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
153                       int real_sx, int real_sy,
154                       int full_sxsize, int full_sysize,
155                       Bitmap *field_save_buffer)
156 {
157   gfx.sx = sx;
158   gfx.sy = sy;
159   gfx.sxsize = sxsize;
160   gfx.sysize = sysize;
161   gfx.real_sx = real_sx;
162   gfx.real_sy = real_sy;
163   gfx.full_sxsize = full_sxsize;
164   gfx.full_sysize = full_sysize;
165
166   gfx.field_save_buffer = field_save_buffer;
167
168   gfx.background_bitmap = NULL;
169   gfx.background_bitmap_mask = REDRAW_NONE;
170
171   SetDrawDeactivationMask(REDRAW_NONE);         /* do not deactivate drawing */
172   SetDrawBackgroundMask(REDRAW_NONE);           /* deactivate masked drawing */
173 }
174
175 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
176 {
177   gfx.dx = dx;
178   gfx.dy = dy;
179   gfx.dxsize = dxsize;
180   gfx.dysize = dysize;
181 }
182
183 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
184 {
185   gfx.vx = vx;
186   gfx.vy = vy;
187   gfx.vxsize = vxsize;
188   gfx.vysize = vysize;
189 }
190
191 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
192 {
193   /* currently only used by MSDOS code to alloc VRAM buffer, if available */
194   gfx.scrollbuffer_width = scrollbuffer_width;
195   gfx.scrollbuffer_height = scrollbuffer_height;
196 }
197
198 void SetDrawDeactivationMask(int draw_deactivation_mask)
199 {
200   gfx.draw_deactivation_mask = draw_deactivation_mask;
201 }
202
203 void SetDrawBackgroundMask(int draw_background_mask)
204 {
205   gfx.draw_background_mask = draw_background_mask;
206 }
207
208 static void DrawBitmapFromTile(Bitmap *bitmap, Bitmap *tile,
209                                int dest_x, int dest_y, int width, int height)
210 {
211   int bitmap_xsize = width;
212   int bitmap_ysize = height;
213   int tile_xsize = tile->width;
214   int tile_ysize = tile->height;
215   int tile_xsteps = (bitmap_xsize + tile_xsize - 1) / tile_xsize;
216   int tile_ysteps = (bitmap_ysize + tile_ysize - 1) / tile_ysize;
217   int x, y;
218
219   for (y = 0; y < tile_ysteps; y++)
220   {
221     for (x = 0; x < tile_xsteps; x++)
222     {
223       int draw_x = dest_x + x * tile_xsize;
224       int draw_y = dest_y + y * tile_ysize;
225       int draw_xsize = MIN(tile_xsize, bitmap_xsize - x * tile_xsize);
226       int draw_ysize = MIN(tile_ysize, bitmap_ysize - y * tile_ysize);
227
228       BlitBitmap(tile, bitmap, 0, 0, draw_xsize, draw_ysize, draw_x, draw_y);
229     }
230   }
231 }
232
233 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
234 {
235   if (background_bitmap_tile != NULL)
236     gfx.background_bitmap_mask |= mask;
237   else
238     gfx.background_bitmap_mask &= ~mask;
239
240   if (gfx.background_bitmap == NULL)
241     gfx.background_bitmap = CreateBitmap(video.width, video.height,
242                                          DEFAULT_DEPTH);
243
244   if (background_bitmap_tile == NULL)   /* empty background requested */
245     return;
246
247   if (mask == REDRAW_FIELD)
248     DrawBitmapFromTile(gfx.background_bitmap, background_bitmap_tile,
249                        gfx.real_sx, gfx.real_sy,
250                        gfx.full_sxsize, gfx.full_sysize);
251   else if (mask == REDRAW_DOOR_1)
252   {
253     DrawBitmapFromTile(gfx.background_bitmap, background_bitmap_tile,
254                        gfx.dx, gfx.dy,
255                        gfx.dxsize, gfx.dysize);
256   }
257 }
258
259 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
260 {
261   SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
262 }
263
264 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
265 {
266   SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
267 }
268
269
270 /* ========================================================================= */
271 /* video functions                                                           */
272 /* ========================================================================= */
273
274 inline static int GetRealDepth(int depth)
275 {
276   return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
277 }
278
279 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
280                                int width, int height, Pixel color)
281 {
282 #if defined(TARGET_SDL)
283   SDLFillRectangle(bitmap, x, y, width, height, color);
284 #else
285   X11FillRectangle(bitmap, x, y, width, height, color);
286 #endif
287 }
288
289 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
290                                int src_x, int src_y, int width, int height,
291                                int dst_x, int dst_y, int mask_mode)
292 {
293 #if defined(TARGET_SDL)
294   SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
295               dst_x, dst_y, mask_mode);
296 #else
297   X11CopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
298               dst_x, dst_y, mask_mode);
299 #endif
300 }
301
302 void InitVideoDisplay(void)
303 {
304 #if defined(TARGET_SDL)
305   SDLInitVideoDisplay();
306 #else
307   X11InitVideoDisplay();
308 #endif
309 }
310
311 void CloseVideoDisplay(void)
312 {
313   KeyboardAutoRepeatOn();
314
315 #if defined(TARGET_SDL)
316   SDL_QuitSubSystem(SDL_INIT_VIDEO);
317 #else
318   if (display)
319     XCloseDisplay(display);
320 #endif
321 }
322
323 void InitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
324                      int width, int height, int depth, boolean fullscreen)
325 {
326   video.width = width;
327   video.height = height;
328   video.depth = GetRealDepth(depth);
329   video.fullscreen_available = FULLSCREEN_STATUS;
330   video.fullscreen_enabled = FALSE;
331   video.fullscreen_modes = NULL;
332   video.fullscreen_mode_current = NULL;
333
334 #if defined(TARGET_SDL)
335   SDLInitVideoBuffer(backbuffer, window, fullscreen);
336 #else
337   X11InitVideoBuffer(backbuffer, window);
338 #endif
339 }
340
341 Bitmap *CreateBitmapStruct(void)
342 {
343 #if defined(TARGET_SDL)
344   return checked_calloc(sizeof(struct SDLSurfaceInfo));
345 #else
346   return checked_calloc(sizeof(struct X11DrawableInfo));
347 #endif
348 }
349
350 Bitmap *CreateBitmap(int width, int height, int depth)
351 {
352   Bitmap *new_bitmap = CreateBitmapStruct();
353   int real_depth = GetRealDepth(depth);
354
355 #if defined(TARGET_SDL)
356   SDLCreateBitmapContent(new_bitmap, width, height, real_depth);
357 #else
358   X11CreateBitmapContent(new_bitmap, width, height, real_depth);
359 #endif
360
361   new_bitmap->width = width;
362   new_bitmap->height = height;
363
364   return new_bitmap;
365 }
366
367 inline static void FreeBitmapPointers(Bitmap *bitmap)
368 {
369   if (bitmap == NULL)
370     return;
371
372 #if defined(TARGET_SDL)
373   SDLFreeBitmapPointers(bitmap);
374 #else
375   X11FreeBitmapPointers(bitmap);
376 #endif
377
378   checked_free(bitmap->source_filename);
379   bitmap->source_filename = NULL;
380 }
381
382 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
383                                           Bitmap *dst_bitmap)
384 {
385   if (src_bitmap == NULL || dst_bitmap == NULL)
386     return;
387
388   FreeBitmapPointers(dst_bitmap);
389
390   *dst_bitmap = *src_bitmap;
391 }
392
393 void FreeBitmap(Bitmap *bitmap)
394 {
395   if (bitmap == NULL)
396     return;
397
398   FreeBitmapPointers(bitmap);
399
400   free(bitmap);
401 }
402
403 void CloseWindow(DrawWindow *window)
404 {
405 #if defined(TARGET_X11)
406   if (window->drawable)
407   {
408     XUnmapWindow(display, window->drawable);
409     XDestroyWindow(display, window->drawable);
410   }
411   if (window->gc)
412     XFreeGC(display, window->gc);
413 #endif
414 }
415
416 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
417                                        int draw_mask)
418 {
419   if (draw_mask == REDRAW_NONE)
420     return FALSE;
421
422   if (draw_mask & REDRAW_ALL)
423     return TRUE;
424
425   if ((draw_mask & REDRAW_FIELD) && x < gfx.real_sx + gfx.full_sxsize)
426     return TRUE;
427
428   if ((draw_mask & REDRAW_DOOR_1) && x >= gfx.dx && y < gfx.dy + gfx.dysize)
429     return TRUE;
430
431   if ((draw_mask & REDRAW_DOOR_2) && x >= gfx.dx && y >= gfx.vy)
432     return TRUE;
433
434   return FALSE;
435 }
436
437 boolean DrawingDeactivated(int x, int y, int width, int height)
438 {
439   return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
440 }
441
442 boolean DrawingOnBackground(int x, int y)
443 {
444   return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
445           CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
446 }
447
448 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
449                 int src_x, int src_y, int width, int height,
450                 int dst_x, int dst_y)
451 {
452   if (DrawingDeactivated(dst_x, dst_y, width, height))
453     return;
454
455   sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
456               dst_x, dst_y, BLIT_OPAQUE);
457 }
458
459 void FadeScreen(Bitmap *bitmap_cross, int fade_mode, int fade_delay,
460                 int post_delay)
461 {
462 #if defined(TARGET_SDL)
463   SDLFadeScreen(bitmap_cross, fade_mode, fade_delay, post_delay);
464 #else
465   X11FadeScreen(bitmap_cross, fade_mode, fade_delay, post_delay);
466 #endif
467 }
468
469 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
470                    Pixel color)
471 {
472   if (DrawingDeactivated(x, y, width, height))
473     return;
474
475   sysFillRectangle(bitmap, x, y, width, height, color);
476 }
477
478 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
479 {
480   FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
481 }
482
483 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
484                                 int width, int height)
485 {
486   if (DrawingOnBackground(x, y))
487     BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
488   else
489     ClearRectangle(bitmap, x, y, width, height);
490 }
491
492 void SetClipMask(Bitmap *bitmap, GC clip_gc, Pixmap clip_pixmap)
493 {
494 #if defined(TARGET_X11)
495   if (clip_gc)
496   {
497     bitmap->clip_gc = clip_gc;
498     XSetClipMask(display, bitmap->clip_gc, clip_pixmap);
499   }
500 #endif
501 }
502
503 void SetClipOrigin(Bitmap *bitmap, GC clip_gc, int clip_x, int clip_y)
504 {
505 #if defined(TARGET_X11)
506   if (clip_gc)
507   {
508     bitmap->clip_gc = clip_gc;
509     XSetClipOrigin(display, bitmap->clip_gc, clip_x, clip_y);
510   }
511 #endif
512 }
513
514 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
515                       int src_x, int src_y, int width, int height,
516                       int dst_x, int dst_y)
517 {
518   if (DrawingDeactivated(dst_x, dst_y, width, height))
519     return;
520
521   sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
522               dst_x, dst_y, BLIT_MASKED);
523 }
524
525 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
526                             int src_x, int src_y, int width, int height,
527                             int dst_x, int dst_y)
528 {
529   if (DrawingOnBackground(dst_x, dst_y))
530   {
531     /* draw background */
532     BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
533                dst_x, dst_y);
534
535     /* draw foreground */
536     SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
537                   dst_x - src_x, dst_y - src_y);
538     BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
539                      dst_x, dst_y);
540   }
541   else
542     BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
543                dst_x, dst_y);
544 }
545
546 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
547                          int to_x, int to_y)
548 {
549 #if defined(TARGET_SDL)
550   SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
551 #else
552   X11DrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
553 #endif
554 }
555
556 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
557                          int to_x, int to_y)
558 {
559 #if defined(TARGET_SDL)
560   SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
561 #else
562   X11DrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
563 #endif
564 }
565
566 #if !defined(TARGET_X11_NATIVE)
567 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
568               int to_x, int to_y, Pixel pixel, int line_width)
569 {
570   int x, y;
571
572   for (x = 0; x < line_width; x++)
573   {
574     for (y = 0; y < line_width; y++)
575     {
576       int dx = x - line_width / 2;
577       int dy = y - line_width / 2;
578
579       if ((x == 0 && y == 0) ||
580           (x == 0 && y == line_width - 1) ||
581           (x == line_width - 1 && y == 0) ||
582           (x == line_width - 1 && y == line_width - 1))
583         continue;
584
585 #if defined(TARGET_SDL)
586       SDLDrawLine(bitmap,
587                   from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
588 #elif defined(TARGET_ALLEGRO)
589       AllegroDrawLine(bitmap->drawable, from_x + dx, from_y + dy,
590                       to_x + dx, to_y + dy, pixel);
591 #endif
592     }
593   }
594 }
595 #endif
596
597 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
598 {
599 #if !defined(TARGET_X11_NATIVE)
600   int line_width = 4;
601   int i;
602
603   for (i = 0; i < num_points - 1; i++)
604     DrawLine(bitmap, points[i].x, points[i].y,
605              points[i + 1].x, points[i + 1].y, pixel, line_width);
606
607   /*
608   SDLDrawLines(bitmap->surface, points, num_points, pixel);
609   */
610 #else
611   XSetForeground(display, bitmap->line_gc[1], pixel);
612   XDrawLines(display, bitmap->drawable, bitmap->line_gc[1],
613              (XPoint *)points, num_points, CoordModeOrigin);
614 #endif
615 }
616
617 Pixel GetPixel(Bitmap *bitmap, int x, int y)
618 {
619   if (x < 0 || x >= bitmap->width ||
620       y < 0 || y >= bitmap->height)
621     return BLACK_PIXEL;
622
623 #if defined(TARGET_SDL)
624   return SDLGetPixel(bitmap, x, y);
625 #elif defined(TARGET_ALLEGRO)
626   return AllegroGetPixel(bitmap->drawable, x, y);
627 #else
628   return X11GetPixel(bitmap, x, y);
629 #endif
630 }
631
632 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
633                       unsigned int color_g, unsigned int color_b)
634 {
635 #if defined(TARGET_SDL)
636   return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
637 #elif defined(TARGET_ALLEGRO)
638   return AllegroAllocColorCell(color_r << 8, color_g << 8, color_b << 8);
639 #else
640   return X11GetPixelFromRGB(color_r, color_g, color_b);
641 #endif
642 }
643
644 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
645 {
646   unsigned int color_r = (color >> 16) & 0xff;
647   unsigned int color_g = (color >>  8) & 0xff;
648   unsigned int color_b = (color >>  0) & 0xff;
649
650   return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
651 }
652
653 /* execute all pending screen drawing operations */
654 void FlushDisplay(void)
655 {
656 #ifndef TARGET_SDL
657   XFlush(display);
658 #endif
659 }
660
661 /* execute and wait for all pending screen drawing operations */
662 void SyncDisplay(void)
663 {
664 #ifndef TARGET_SDL
665   XSync(display, FALSE);
666 #endif
667 }
668
669 void KeyboardAutoRepeatOn(void)
670 {
671 #if defined(TARGET_SDL)
672   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
673                       SDL_DEFAULT_REPEAT_INTERVAL / 2);
674   SDL_EnableUNICODE(1);
675 #else
676   if (display)
677     XAutoRepeatOn(display);
678 #endif
679 }
680
681 void KeyboardAutoRepeatOff(void)
682 {
683 #if defined(TARGET_SDL)
684   SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
685   SDL_EnableUNICODE(0);
686 #else
687   if (display)
688     XAutoRepeatOff(display);
689 #endif
690 }
691
692 boolean PointerInWindow(DrawWindow *window)
693 {
694 #if defined(TARGET_SDL)
695   return TRUE;
696 #else
697   Window root, child;
698   int root_x, root_y;
699   unsigned int mask;
700   int win_x, win_y;
701
702   /* if XQueryPointer() returns False, the pointer
703      is not on the same screen as the specified window */
704   return XQueryPointer(display, window->drawable, &root, &child,
705                        &root_x, &root_y, &win_x, &win_y, &mask);
706 #endif
707 }
708
709 boolean SetVideoMode(boolean fullscreen)
710 {
711 #if defined(TARGET_SDL)
712   return SDLSetVideoMode(&backbuffer, fullscreen);
713 #else
714   boolean success = TRUE;
715
716   if (fullscreen && video.fullscreen_available)
717   {
718     Error(ERR_WARN, "fullscreen not available in X11 version");
719
720     /* display error message only once */
721     video.fullscreen_available = FALSE;
722
723     success = FALSE;
724   }
725
726   return success;
727 #endif
728 }
729
730 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
731 {
732 #if defined(TARGET_SDL)
733   if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
734       (!fullscreen && video.fullscreen_enabled))
735     fullscreen = SetVideoMode(fullscreen);
736 #endif
737
738   return fullscreen;
739 }
740
741 Bitmap *LoadImage(char *filename)
742 {
743   Bitmap *new_bitmap;
744
745 #if defined(TARGET_SDL)
746   new_bitmap = SDLLoadImage(filename);
747 #else
748   new_bitmap = X11LoadImage(filename);
749 #endif
750
751   if (new_bitmap)
752     new_bitmap->source_filename = getStringCopy(filename);
753
754   return new_bitmap;
755 }
756
757 Bitmap *LoadCustomImage(char *basename)
758 {
759   char *filename = getCustomImageFilename(basename);
760   Bitmap *new_bitmap;
761
762   if (filename == NULL)
763     Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
764
765   if ((new_bitmap = LoadImage(filename)) == NULL)
766     Error(ERR_EXIT, "LoadImage() failed: %s", GetError());
767
768   return new_bitmap;
769 }
770
771 void ReloadCustomImage(Bitmap *bitmap, char *basename)
772 {
773   char *filename = getCustomImageFilename(basename);
774   Bitmap *new_bitmap;
775
776   if (filename == NULL)         /* (should never happen) */
777   {
778     Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
779     return;
780   }
781
782   if (strEqual(filename, bitmap->source_filename))
783   {
784     /* The old and new image are the same (have the same filename and path).
785        This usually means that this image does not exist in this graphic set
786        and a fallback to the existing image is done. */
787
788     return;
789   }
790
791   if ((new_bitmap = LoadImage(filename)) == NULL)
792   {
793     Error(ERR_WARN, "LoadImage() failed: %s", GetError());
794     return;
795   }
796
797   if (bitmap->width != new_bitmap->width ||
798       bitmap->height != new_bitmap->height)
799   {
800     Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
801           filename);
802     FreeBitmap(new_bitmap);
803     return;
804   }
805
806   TransferBitmapPointers(new_bitmap, bitmap);
807   free(new_bitmap);
808 }
809
810 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
811 {
812   Bitmap *dst_bitmap = CreateBitmap(zoom_width, zoom_height, DEFAULT_DEPTH);
813
814 #if defined(TARGET_SDL)
815   SDLZoomBitmap(src_bitmap, dst_bitmap);
816 #else
817   X11ZoomBitmap(src_bitmap, dst_bitmap);
818 #endif
819
820   return dst_bitmap;
821 }
822
823 static void CreateScaledBitmaps(Bitmap *old_bitmap, int zoom_factor,
824                                 boolean create_small_bitmaps)
825 {
826   Bitmap swap_bitmap;
827   Bitmap *new_bitmap, *tmp_bitmap_1, *tmp_bitmap_2, *tmp_bitmap_4,*tmp_bitmap_8;
828   int width_1, height_1, width_2, height_2, width_4, height_4, width_8,height_8;
829   int new_width, new_height;
830
831   /* calculate new image dimensions for normal sized image */
832   width_1  = old_bitmap->width  * zoom_factor;
833   height_1 = old_bitmap->height * zoom_factor;
834
835   /* get image with normal size (this might require scaling up) */
836   if (zoom_factor != 1)
837     tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
838   else
839     tmp_bitmap_1 = old_bitmap;
840
841   /* this is only needed to make compilers happy */
842   tmp_bitmap_2 = tmp_bitmap_4 = tmp_bitmap_8 = NULL;
843
844   if (create_small_bitmaps)
845   {
846     /* calculate new image dimensions for small images */
847     width_2  = width_1  / 2;
848     height_2 = height_1 / 2;
849     width_4  = width_1  / 4;
850     height_4 = height_1 / 4;
851     width_8  = width_1  / 8;
852     height_8 = height_1 / 8;
853
854     /* get image with 1/2 of normal size (for use in the level editor) */
855     if (zoom_factor != 2)
856       tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_1 / 2, height_1 / 2);
857     else
858       tmp_bitmap_2 = old_bitmap;
859
860     /* get image with 1/4 of normal size (for use in the level editor) */
861     if (zoom_factor != 4)
862       tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_2 / 2, height_2 / 2);
863     else
864       tmp_bitmap_4 = old_bitmap;
865
866     /* get image with 1/8 of normal size (for use on the preview screen) */
867     if (zoom_factor != 8)
868       tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_4 / 2, height_4 / 2);
869     else
870       tmp_bitmap_8 = old_bitmap;
871   }
872
873   /* if image was scaled up, create new clipmask for normal size image */
874   if (zoom_factor != 1)
875   {
876 #if defined(TARGET_X11)
877     if (old_bitmap->clip_mask)
878       XFreePixmap(display, old_bitmap->clip_mask);
879
880     old_bitmap->clip_mask =
881       Pixmap_to_Mask(tmp_bitmap_1->drawable, width_1, height_1);
882
883     XSetClipMask(display, old_bitmap->stored_clip_gc, old_bitmap->clip_mask);
884 #else
885     SDL_Surface *tmp_surface_1 = tmp_bitmap_1->surface;
886
887     if (old_bitmap->surface_masked)
888       SDL_FreeSurface(old_bitmap->surface_masked);
889
890     SDL_SetColorKey(tmp_surface_1, SDL_SRCCOLORKEY,
891                     SDL_MapRGB(tmp_surface_1->format, 0x00, 0x00, 0x00));
892     if ((old_bitmap->surface_masked = SDL_DisplayFormat(tmp_surface_1)) ==NULL)
893       Error(ERR_EXIT, "SDL_DisplayFormat() failed");
894     SDL_SetColorKey(tmp_surface_1, 0, 0);       /* reset transparent pixel */
895 #endif
896   }
897
898   if (create_small_bitmaps)
899   {
900     new_width  = width_1;
901     new_height = height_1 + (height_1 + 1) / 2;     /* prevent odd height */
902
903     new_bitmap = CreateBitmap(new_width, new_height, DEFAULT_DEPTH);
904
905     BlitBitmap(tmp_bitmap_1, new_bitmap, 0, 0, width_1, height_1, 0, 0);
906     BlitBitmap(tmp_bitmap_2, new_bitmap, 0, 0, width_1 / 2, height_1 / 2,
907                0, height_1);
908     BlitBitmap(tmp_bitmap_4, new_bitmap, 0, 0, width_1 / 4, height_1 / 4,
909                width_1 / 2, height_1);
910     BlitBitmap(tmp_bitmap_8, new_bitmap, 0, 0, width_1 / 8, height_1 / 8,
911                3 * width_1 / 4, height_1);
912   }
913   else
914   {
915     new_width  = width_1;
916     new_height = height_1;
917
918     new_bitmap = tmp_bitmap_1;  /* directly use tmp_bitmap_1 as new bitmap */
919   }
920
921   if (create_small_bitmaps)
922   {
923     /* if no small bitmaps created, tmp_bitmap_1 is used as new bitmap now */
924     if (zoom_factor != 1)
925       FreeBitmap(tmp_bitmap_1);
926
927     if (zoom_factor != 2)
928       FreeBitmap(tmp_bitmap_2);
929
930     if (zoom_factor != 4)
931       FreeBitmap(tmp_bitmap_4);
932
933     if (zoom_factor != 8)
934       FreeBitmap(tmp_bitmap_8);
935   }
936
937   /* replace image with extended image (containing 1/1, 1/2, 1/4, 1/8 size) */
938 #if defined(TARGET_SDL)
939   swap_bitmap.surface = old_bitmap->surface;
940   old_bitmap->surface = new_bitmap->surface;
941   new_bitmap->surface = swap_bitmap.surface;
942 #else
943   swap_bitmap.drawable = old_bitmap->drawable;
944   old_bitmap->drawable = new_bitmap->drawable;
945   new_bitmap->drawable = swap_bitmap.drawable;
946 #endif
947
948   old_bitmap->width  = new_bitmap->width;
949   old_bitmap->height = new_bitmap->height;
950
951   FreeBitmap(new_bitmap);       /* this actually frees the _old_ bitmap now */
952 }
953
954 void CreateBitmapWithSmallBitmaps(Bitmap *old_bitmap, int zoom_factor)
955 {
956   CreateScaledBitmaps(old_bitmap, zoom_factor, TRUE);
957 }
958
959 void ScaleBitmap(Bitmap *old_bitmap, int zoom_factor)
960 {
961   CreateScaledBitmaps(old_bitmap, zoom_factor, FALSE);
962 }
963
964
965 /* ------------------------------------------------------------------------- */
966 /* mouse pointer functions                                                   */
967 /* ------------------------------------------------------------------------- */
968
969 #if !defined(PLATFORM_MSDOS)
970 /* XPM */
971 static const char *cursor_image_playfield[] =
972 {
973   /* width height num_colors chars_per_pixel */
974   "    16    16        3            1",
975
976   /* colors */
977   "X c #000000",
978   ". c #ffffff",
979   "  c None",
980
981 #if 1
982   /* some people complained about a "white dot" on the screen and thought it
983      was a graphical error... OK, let's just remove the whole pointer :-) */
984
985   /* pixels */
986   "                ",
987   "                ",
988   "                ",
989   "                ",
990   "                ",
991   "                ",
992   "                ",
993   "                ",
994   "                ",
995   "                ",
996   "                ",
997   "                ",
998   "                ",
999   "                ",
1000   "                ",
1001   "                ",
1002
1003   /* hot spot */
1004   "0,0"
1005
1006 #else
1007
1008   /* pixels */
1009   " X              ",
1010   "X.X             ",
1011   " X              ",
1012   "                ",
1013   "                ",
1014   "                ",
1015   "                ",
1016   "                ",
1017   "                ",
1018   "                ",
1019   "                ",
1020   "                ",
1021   "                ",
1022   "                ",
1023   "                ",
1024   "                ",
1025
1026   /* hot spot */
1027   "1,1"
1028 #endif
1029 };
1030
1031 #if defined(TARGET_SDL)
1032 static const int cursor_bit_order = BIT_ORDER_MSB;
1033 #elif defined(TARGET_X11_NATIVE)
1034 static const int cursor_bit_order = BIT_ORDER_LSB;
1035 #endif
1036
1037 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1038 {
1039   struct MouseCursorInfo *cursor;
1040   boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1041   int header_lines = 4;
1042   int x, y, i;
1043
1044   cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1045
1046   sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1047
1048   i = -1;
1049   for (y = 0; y < cursor->width; y++)
1050   {
1051     for (x = 0; x < cursor->height; x++)
1052     {
1053       int bit_nr = x % 8;
1054       int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1055
1056       if (bit_nr == 0)
1057       {
1058         i++;
1059         cursor->data[i] = cursor->mask[i] = 0;
1060       }
1061
1062       switch (image[header_lines + y][x])
1063       {
1064         case 'X':
1065           cursor->data[i] |= bit_mask;
1066           cursor->mask[i] |= bit_mask;
1067           break;
1068
1069         case '.':
1070           cursor->mask[i] |= bit_mask;
1071           break;
1072
1073         case ' ':
1074           break;
1075       }
1076     }
1077   }
1078
1079   sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1080
1081   return cursor;
1082 }
1083 #endif  /* !PLATFORM_MSDOS */
1084
1085 void SetMouseCursor(int mode)
1086 {
1087 #if !defined(PLATFORM_MSDOS)
1088   static struct MouseCursorInfo *cursor_playfield = NULL;
1089
1090   if (cursor_playfield == NULL)
1091     cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1092
1093 #if defined(TARGET_SDL)
1094   SDLSetMouseCursor(mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1095 #elif defined(TARGET_X11_NATIVE)
1096   X11SetMouseCursor(mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1097 #endif
1098 #endif
1099 }
1100
1101
1102 /* ========================================================================= */
1103 /* audio functions                                                           */
1104 /* ========================================================================= */
1105
1106 void OpenAudio(void)
1107 {
1108   /* always start with reliable default values */
1109   audio.sound_available = FALSE;
1110   audio.music_available = FALSE;
1111   audio.loops_available = FALSE;
1112
1113   audio.sound_enabled = FALSE;
1114   audio.sound_deactivated = FALSE;
1115
1116   audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1117   audio.mixer_pid = 0;
1118   audio.device_name = NULL;
1119   audio.device_fd = -1;
1120
1121   audio.num_channels = 0;
1122   audio.music_channel = 0;
1123   audio.first_sound_channel = 0;
1124
1125 #if defined(TARGET_SDL)
1126   SDLOpenAudio();
1127 #elif defined(PLATFORM_UNIX)
1128   UnixOpenAudio();
1129 #elif defined(PLATFORM_MSDOS)
1130   MSDOSOpenAudio();
1131 #endif
1132 }
1133
1134 void CloseAudio(void)
1135 {
1136 #if defined(TARGET_SDL)
1137   SDLCloseAudio();
1138 #elif defined(PLATFORM_UNIX)
1139   UnixCloseAudio();
1140 #elif defined(PLATFORM_MSDOS)
1141   MSDOSCloseAudio();
1142 #endif
1143
1144   audio.sound_enabled = FALSE;
1145 }
1146
1147 void SetAudioMode(boolean enabled)
1148 {
1149   if (!audio.sound_available)
1150     return;
1151
1152   audio.sound_enabled = enabled;
1153 }
1154
1155
1156 /* ========================================================================= */
1157 /* event functions                                                           */
1158 /* ========================================================================= */
1159
1160 void InitEventFilter(EventFilter filter_function)
1161 {
1162 #if defined(TARGET_SDL)
1163   /* set event filter to filter out certain events */
1164   SDL_SetEventFilter(filter_function);
1165 #endif
1166 }
1167
1168 boolean PendingEvent(void)
1169 {
1170 #if defined(TARGET_SDL)
1171   return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1172 #else
1173   return (XPending(display) ? TRUE : FALSE);
1174 #endif
1175 }
1176
1177 void NextEvent(Event *event)
1178 {
1179 #if defined(TARGET_SDL)
1180   SDLNextEvent(event);
1181 #else
1182   XNextEvent(display, event);
1183 #endif
1184 }
1185
1186 void PeekEvent(Event *event)
1187 {
1188 #if defined(TARGET_SDL)
1189   SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1190 #else
1191   XPeekEvent(display, event);
1192 #endif
1193 }
1194
1195 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1196 {
1197 #if defined(TARGET_SDL)
1198
1199 #if 0
1200   printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1201          (int)event->keysym.unicode,
1202          (int)event->keysym.sym,
1203          (int)SDL_GetModState());
1204 #endif
1205
1206   if (with_modifiers &&
1207       event->keysym.unicode > 0x0000 &&
1208       event->keysym.unicode < 0x2000)
1209     return event->keysym.unicode;
1210   else
1211     return event->keysym.sym;
1212
1213 #else
1214
1215 #if 0
1216   printf("with modifiers == '0x%04x', without modifiers == '0x%04x'\n",
1217          (int)XLookupKeysym(event, event->state),
1218          (int)XLookupKeysym(event, 0));
1219 #endif
1220
1221   if (with_modifiers)
1222     return XLookupKeysym(event, event->state);
1223   else
1224     return XLookupKeysym(event, 0);
1225 #endif
1226 }
1227
1228 KeyMod HandleKeyModState(Key key, int key_status)
1229 {
1230   static KeyMod current_modifiers = KMOD_None;
1231
1232   if (key != KSYM_UNDEFINED)    /* new key => check for modifier key change */
1233   {
1234     KeyMod new_modifier = KMOD_None;
1235
1236     switch(key)
1237     {
1238       case KSYM_Shift_L:
1239         new_modifier = KMOD_Shift_L;
1240         break;
1241       case KSYM_Shift_R:
1242         new_modifier = KMOD_Shift_R;
1243         break;
1244       case KSYM_Control_L:
1245         new_modifier = KMOD_Control_L;
1246         break;
1247       case KSYM_Control_R:
1248         new_modifier = KMOD_Control_R;
1249         break;
1250       case KSYM_Meta_L:
1251         new_modifier = KMOD_Meta_L;
1252         break;
1253       case KSYM_Meta_R:
1254         new_modifier = KMOD_Meta_R;
1255         break;
1256       case KSYM_Alt_L:
1257         new_modifier = KMOD_Alt_L;
1258         break;
1259       case KSYM_Alt_R:
1260         new_modifier = KMOD_Alt_R;
1261         break;
1262       default:
1263         break;
1264     }
1265
1266     if (key_status == KEY_PRESSED)
1267       current_modifiers |= new_modifier;
1268     else
1269       current_modifiers &= ~new_modifier;
1270   }
1271
1272   return current_modifiers;
1273 }
1274
1275 KeyMod GetKeyModState()
1276 {
1277 #if defined(TARGET_SDL)
1278   return (KeyMod)SDL_GetModState();
1279 #else
1280   return HandleKeyModState(KSYM_UNDEFINED, 0);
1281 #endif
1282 }
1283
1284 KeyMod GetKeyModStateFromEvents()
1285 {
1286   /* always use key modifier state as tracked from key events (this is needed
1287      if the modifier key event was injected into the event queue, but the key
1288      was not really pressed on keyboard -- SDL_GetModState() seems to directly
1289      query the keys as held pressed on the keyboard) -- this case is currently
1290      only used to filter out clipboard insert events from "True X-Mouse" tool */
1291
1292   return HandleKeyModState(KSYM_UNDEFINED, 0);
1293 }
1294
1295 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1296 {
1297   if (event->type != EVENT_CLIENTMESSAGE)
1298     return FALSE;
1299
1300 #if defined(TARGET_SDL)
1301   return TRUE;          /* the only possible message here is SDL_QUIT */
1302 #elif defined(PLATFORM_UNIX)
1303   if ((event->window == window->drawable) &&
1304       (event->data.l[0] == XInternAtom(display, "WM_DELETE_WINDOW", FALSE)))
1305     return TRUE;
1306 #endif
1307
1308   return FALSE;
1309 }
1310
1311
1312 /* ========================================================================= */
1313 /* joystick functions                                                        */
1314 /* ========================================================================= */
1315
1316 void InitJoysticks()
1317 {
1318   int i;
1319
1320 #if defined(NO_JOYSTICK)
1321   return;       /* joysticks generally deactivated by compile-time directive */
1322 #endif
1323
1324   /* always start with reliable default values */
1325   joystick.status = JOYSTICK_NOT_AVAILABLE;
1326   for (i = 0; i < MAX_PLAYERS; i++)
1327     joystick.fd[i] = -1;                /* joystick device closed */
1328
1329 #if defined(TARGET_SDL)
1330   SDLInitJoysticks();
1331 #elif defined(PLATFORM_UNIX)
1332   UnixInitJoysticks();
1333 #elif defined(PLATFORM_MSDOS)
1334   MSDOSInitJoysticks();
1335 #endif
1336
1337 #if 0
1338   for (i = 0; i < MAX_PLAYERS; i++)
1339     printf("::: Joystick for player %d: %d\n", i, joystick.fd[i]);
1340 #endif
1341 }
1342
1343 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1344 {
1345 #if defined(TARGET_SDL)
1346   return SDLReadJoystick(nr, x, y, b1, b2);
1347 #elif defined(PLATFORM_UNIX)
1348   return UnixReadJoystick(nr, x, y, b1, b2);
1349 #elif defined(PLATFORM_MSDOS)
1350   return MSDOSReadJoystick(nr, x, y, b1, b2);
1351 #endif
1352 }