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