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