rocksndiamonds-3.2.6.1
[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 InitGfxWindowInfo(int win_xsize, int win_ysize)
193 {
194   gfx.win_xsize = win_xsize;
195   gfx.win_ysize = win_ysize;
196 }
197
198 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
199 {
200   /* currently only used by MSDOS code to alloc VRAM buffer, if available */
201   /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
202   gfx.scrollbuffer_width = scrollbuffer_width;
203   gfx.scrollbuffer_height = scrollbuffer_height;
204 }
205
206 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
207 {
208   gfx.draw_busy_anim_function = draw_busy_anim_function;
209 }
210
211 void InitGfxCustomArtworkInfo()
212 {
213   gfx.override_level_graphics = FALSE;
214   gfx.override_level_sounds = FALSE;
215   gfx.override_level_music = FALSE;
216
217   gfx.draw_init_text = TRUE;
218 }
219
220 void SetDrawDeactivationMask(int draw_deactivation_mask)
221 {
222   gfx.draw_deactivation_mask = draw_deactivation_mask;
223 }
224
225 void SetDrawBackgroundMask(int draw_background_mask)
226 {
227   gfx.draw_background_mask = draw_background_mask;
228 }
229
230 static void DrawBitmapFromTile(Bitmap *bitmap, Bitmap *tile,
231                                int dest_x, int dest_y, int width, int height)
232 {
233   int bitmap_xsize = width;
234   int bitmap_ysize = height;
235   int tile_xsize = tile->width;
236   int tile_ysize = tile->height;
237   int tile_xsteps = (bitmap_xsize + tile_xsize - 1) / tile_xsize;
238   int tile_ysteps = (bitmap_ysize + tile_ysize - 1) / tile_ysize;
239   int x, y;
240
241   for (y = 0; y < tile_ysteps; y++)
242   {
243     for (x = 0; x < tile_xsteps; x++)
244     {
245       int draw_x = dest_x + x * tile_xsize;
246       int draw_y = dest_y + y * tile_ysize;
247       int draw_xsize = MIN(tile_xsize, bitmap_xsize - x * tile_xsize);
248       int draw_ysize = MIN(tile_ysize, bitmap_ysize - y * tile_ysize);
249
250       BlitBitmap(tile, bitmap, 0, 0, draw_xsize, draw_ysize, draw_x, draw_y);
251     }
252   }
253 }
254
255 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
256 {
257   if (background_bitmap_tile != NULL)
258     gfx.background_bitmap_mask |= mask;
259   else
260     gfx.background_bitmap_mask &= ~mask;
261
262   if (gfx.background_bitmap == NULL)
263     gfx.background_bitmap = CreateBitmap(video.width, video.height,
264                                          DEFAULT_DEPTH);
265
266   if (background_bitmap_tile == NULL)   /* empty background requested */
267     return;
268
269   if (mask == REDRAW_ALL)
270     DrawBitmapFromTile(gfx.background_bitmap, background_bitmap_tile,
271                        0, 0, video.width, video.height);
272   else if (mask == REDRAW_FIELD)
273     DrawBitmapFromTile(gfx.background_bitmap, background_bitmap_tile,
274                        gfx.real_sx, gfx.real_sy,
275                        gfx.full_sxsize, gfx.full_sysize);
276   else if (mask == REDRAW_DOOR_1)
277   {
278     DrawBitmapFromTile(gfx.background_bitmap, background_bitmap_tile,
279                        gfx.dx, gfx.dy,
280                        gfx.dxsize, gfx.dysize);
281   }
282 }
283
284 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
285 {
286   /* remove every mask before setting mask for window */
287   /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
288   SetBackgroundBitmap(NULL, 0xffff);            /* !!! FIX THIS !!! */
289   SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
290 }
291
292 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
293 {
294   /* remove window area mask before setting mask for main area */
295   /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
296   SetBackgroundBitmap(NULL, REDRAW_ALL);        /* !!! FIX THIS !!! */
297   SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
298 }
299
300 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
301 {
302   /* remove window area mask before setting mask for door area */
303   /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
304   SetBackgroundBitmap(NULL, REDRAW_ALL);        /* !!! FIX THIS !!! */
305   SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
306 }
307
308
309 /* ========================================================================= */
310 /* video functions                                                           */
311 /* ========================================================================= */
312
313 inline static int GetRealDepth(int depth)
314 {
315   return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
316 }
317
318 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
319                                int width, int height, Pixel color)
320 {
321 #if defined(TARGET_SDL)
322   SDLFillRectangle(bitmap, x, y, width, height, color);
323 #else
324   X11FillRectangle(bitmap, x, y, width, height, color);
325 #endif
326 }
327
328 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
329                                int src_x, int src_y, int width, int height,
330                                int dst_x, int dst_y, int mask_mode)
331 {
332 #if defined(TARGET_SDL)
333   SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
334               dst_x, dst_y, mask_mode);
335 #else
336   X11CopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
337               dst_x, dst_y, mask_mode);
338 #endif
339 }
340
341 void InitVideoDisplay(void)
342 {
343 #if defined(TARGET_SDL)
344   SDLInitVideoDisplay();
345 #else
346   X11InitVideoDisplay();
347 #endif
348 }
349
350 void CloseVideoDisplay(void)
351 {
352   KeyboardAutoRepeatOn();
353
354 #if defined(TARGET_SDL)
355   SDL_QuitSubSystem(SDL_INIT_VIDEO);
356 #else
357   if (display)
358     XCloseDisplay(display);
359 #endif
360 }
361
362 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
363 {
364   video.width = width;
365   video.height = height;
366   video.depth = GetRealDepth(depth);
367
368   video.fullscreen_available = FULLSCREEN_STATUS;
369   video.fullscreen_enabled = FALSE;
370   video.fullscreen_modes = NULL;
371   video.fullscreen_mode_current = NULL;
372
373 #if defined(TARGET_SDL)
374   SDLInitVideoBuffer(&backbuffer, &window, fullscreen);
375 #else
376   X11InitVideoBuffer(&backbuffer, &window);
377 #endif
378
379   drawto = backbuffer;
380 }
381
382 Bitmap *CreateBitmapStruct(void)
383 {
384 #if defined(TARGET_SDL)
385   return checked_calloc(sizeof(struct SDLSurfaceInfo));
386 #else
387   return checked_calloc(sizeof(struct X11DrawableInfo));
388 #endif
389 }
390
391 Bitmap *CreateBitmap(int width, int height, int depth)
392 {
393   Bitmap *new_bitmap = CreateBitmapStruct();
394   int real_width  = MAX(1, width);      /* prevent zero bitmap width */
395   int real_height = MAX(1, height);     /* prevent zero bitmap height */
396   int real_depth  = GetRealDepth(depth);
397
398 #if defined(TARGET_SDL)
399   SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
400 #else
401   X11CreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
402 #endif
403
404   new_bitmap->width  = real_width;
405   new_bitmap->height = real_height;
406
407   return new_bitmap;
408 }
409
410 inline static void FreeBitmapPointers(Bitmap *bitmap)
411 {
412   if (bitmap == NULL)
413     return;
414
415 #if defined(TARGET_SDL)
416   SDLFreeBitmapPointers(bitmap);
417 #else
418   X11FreeBitmapPointers(bitmap);
419 #endif
420
421   checked_free(bitmap->source_filename);
422   bitmap->source_filename = NULL;
423 }
424
425 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
426                                           Bitmap *dst_bitmap)
427 {
428   if (src_bitmap == NULL || dst_bitmap == NULL)
429     return;
430
431   FreeBitmapPointers(dst_bitmap);
432
433   *dst_bitmap = *src_bitmap;
434 }
435
436 void FreeBitmap(Bitmap *bitmap)
437 {
438   if (bitmap == NULL)
439     return;
440
441   FreeBitmapPointers(bitmap);
442
443   free(bitmap);
444 }
445
446 void CloseWindow(DrawWindow *window)
447 {
448 #if defined(TARGET_X11)
449   if (window->drawable)
450   {
451     XUnmapWindow(display, window->drawable);
452     XDestroyWindow(display, window->drawable);
453   }
454   if (window->gc)
455     XFreeGC(display, window->gc);
456 #endif
457 }
458
459 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
460                                        int draw_mask)
461 {
462   if (draw_mask == REDRAW_NONE)
463     return FALSE;
464
465   if (draw_mask & REDRAW_ALL)
466     return TRUE;
467
468   if ((draw_mask & REDRAW_FIELD) &&
469       x >= gfx.real_sx && x < gfx.real_sx + gfx.full_sxsize)
470     return TRUE;
471
472   if ((draw_mask & REDRAW_DOOR_1) &&
473       x >= gfx.dx && y < gfx.dy + gfx.dysize)
474     return TRUE;
475
476   if ((draw_mask & REDRAW_DOOR_2) &&
477       x >= gfx.dx && y >= gfx.vy)
478     return TRUE;
479
480   return FALSE;
481 }
482
483 boolean DrawingDeactivated(int x, int y, int width, int height)
484 {
485   return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
486 }
487
488 boolean DrawingOnBackground(int x, int y)
489 {
490   return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
491           CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
492 }
493
494 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
495                 int src_x, int src_y, int width, int height,
496                 int dst_x, int dst_y)
497 {
498   if (DrawingDeactivated(dst_x, dst_y, width, height))
499     return;
500
501 #if 0
502   /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
503   /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
504      but is already fixed in SVN and should therefore finally be fixed with
505      the next official SDL release, which is probably version 1.2.14.) */
506 #if 1
507   /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
508 #if defined(TARGET_SDL) && defined(PLATFORM_WIN32)
509   if (src_bitmap == dst_bitmap)
510   {
511     /* !!! THIS IS A BUG (IN THE SDL LIBRARY?) AND SHOULD BE FIXED !!! */
512
513     /* needed when blitting directly to same bitmap -- should not be needed with
514        recent SDL libraries, but apparently does not work in 1.2.11 directly */
515
516     static Bitmap *tmp_bitmap = NULL;
517     static int tmp_bitmap_xsize = 0;
518     static int tmp_bitmap_ysize = 0;
519
520     /* start with largest static bitmaps for initial bitmap size ... */
521     if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
522     {
523       tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
524       tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
525     }
526
527     /* ... and allow for later re-adjustments due to custom artwork bitmaps */
528     if (src_bitmap->width > tmp_bitmap_xsize ||
529         src_bitmap->height > tmp_bitmap_ysize)
530     {
531       tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
532       tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
533
534       FreeBitmap(tmp_bitmap);
535
536       tmp_bitmap = NULL;
537     }
538
539     if (tmp_bitmap == NULL)
540       tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
541                                 DEFAULT_DEPTH);
542
543     sysCopyArea(src_bitmap, tmp_bitmap,
544                 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
545     sysCopyArea(tmp_bitmap, dst_bitmap,
546                 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
547
548     return;
549   }
550 #endif
551 #endif
552 #endif
553
554   sysCopyArea(src_bitmap, dst_bitmap,
555               src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
556 }
557
558 void FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
559                    int fade_mode, int fade_delay, int post_delay,
560                    void (*draw_border_function)(void))
561 {
562 #if defined(TARGET_SDL)
563   SDLFadeRectangle(bitmap_cross, x, y, width, height,
564                    fade_mode, fade_delay, post_delay, draw_border_function);
565 #else
566   X11FadeRectangle(bitmap_cross, x, y, width, height,
567                    fade_mode, fade_delay, post_delay, draw_border_function);
568 #endif
569 }
570
571 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
572                    Pixel color)
573 {
574   if (DrawingDeactivated(x, y, width, height))
575     return;
576
577   sysFillRectangle(bitmap, x, y, width, height, color);
578 }
579
580 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
581 {
582   FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
583 }
584
585 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
586                                 int width, int height)
587 {
588   if (DrawingOnBackground(x, y))
589     BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
590   else
591     ClearRectangle(bitmap, x, y, width, height);
592 }
593
594 void SetClipMask(Bitmap *bitmap, GC clip_gc, Pixmap clip_pixmap)
595 {
596 #if defined(TARGET_X11)
597   if (clip_gc)
598   {
599     bitmap->clip_gc = clip_gc;
600     XSetClipMask(display, bitmap->clip_gc, clip_pixmap);
601   }
602 #endif
603 }
604
605 void SetClipOrigin(Bitmap *bitmap, GC clip_gc, int clip_x, int clip_y)
606 {
607 #if defined(TARGET_X11)
608   if (clip_gc)
609   {
610     bitmap->clip_gc = clip_gc;
611     XSetClipOrigin(display, bitmap->clip_gc, clip_x, clip_y);
612   }
613 #endif
614 }
615
616 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
617                       int src_x, int src_y, int width, int height,
618                       int dst_x, int dst_y)
619 {
620   if (DrawingDeactivated(dst_x, dst_y, width, height))
621     return;
622
623   sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
624               dst_x, dst_y, BLIT_MASKED);
625 }
626
627 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
628                             int src_x, int src_y, int width, int height,
629                             int dst_x, int dst_y)
630 {
631   if (DrawingOnBackground(dst_x, dst_y))
632   {
633     /* draw background */
634     BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
635                dst_x, dst_y);
636
637     /* draw foreground */
638     SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
639                   dst_x - src_x, dst_y - src_y);
640     BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
641                      dst_x, dst_y);
642   }
643   else
644     BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
645                dst_x, dst_y);
646 }
647
648 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
649                          int to_x, int to_y)
650 {
651 #if defined(TARGET_SDL)
652   SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
653 #else
654   X11DrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
655 #endif
656 }
657
658 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
659                          int to_x, int to_y)
660 {
661 #if defined(TARGET_SDL)
662   SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
663 #else
664   X11DrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
665 #endif
666 }
667
668 #if !defined(TARGET_X11_NATIVE)
669 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
670               int to_x, int to_y, Pixel pixel, int line_width)
671 {
672   int x, y;
673
674   for (x = 0; x < line_width; x++)
675   {
676     for (y = 0; y < line_width; y++)
677     {
678       int dx = x - line_width / 2;
679       int dy = y - line_width / 2;
680
681       if ((x == 0 && y == 0) ||
682           (x == 0 && y == line_width - 1) ||
683           (x == line_width - 1 && y == 0) ||
684           (x == line_width - 1 && y == line_width - 1))
685         continue;
686
687 #if defined(TARGET_SDL)
688       SDLDrawLine(bitmap,
689                   from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
690 #elif defined(TARGET_ALLEGRO)
691       AllegroDrawLine(bitmap->drawable, from_x + dx, from_y + dy,
692                       to_x + dx, to_y + dy, pixel);
693 #endif
694     }
695   }
696 }
697 #endif
698
699 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
700 {
701 #if !defined(TARGET_X11_NATIVE)
702   int line_width = 4;
703   int i;
704
705   for (i = 0; i < num_points - 1; i++)
706     DrawLine(bitmap, points[i].x, points[i].y,
707              points[i + 1].x, points[i + 1].y, pixel, line_width);
708
709   /*
710   SDLDrawLines(bitmap->surface, points, num_points, pixel);
711   */
712 #else
713   XSetForeground(display, bitmap->line_gc[1], pixel);
714   XDrawLines(display, bitmap->drawable, bitmap->line_gc[1],
715              (XPoint *)points, num_points, CoordModeOrigin);
716 #endif
717 }
718
719 Pixel GetPixel(Bitmap *bitmap, int x, int y)
720 {
721   if (x < 0 || x >= bitmap->width ||
722       y < 0 || y >= bitmap->height)
723     return BLACK_PIXEL;
724
725 #if defined(TARGET_SDL)
726   return SDLGetPixel(bitmap, x, y);
727 #elif defined(TARGET_ALLEGRO)
728   return AllegroGetPixel(bitmap->drawable, x, y);
729 #else
730   return X11GetPixel(bitmap, x, y);
731 #endif
732 }
733
734 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
735                       unsigned int color_g, unsigned int color_b)
736 {
737 #if defined(TARGET_SDL)
738   return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
739 #elif defined(TARGET_ALLEGRO)
740   return AllegroAllocColorCell(color_r << 8, color_g << 8, color_b << 8);
741 #else
742   return X11GetPixelFromRGB(color_r, color_g, color_b);
743 #endif
744 }
745
746 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
747 {
748   unsigned int color_r = (color >> 16) & 0xff;
749   unsigned int color_g = (color >>  8) & 0xff;
750   unsigned int color_b = (color >>  0) & 0xff;
751
752   return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
753 }
754
755 /* execute all pending screen drawing operations */
756 void FlushDisplay(void)
757 {
758 #ifndef TARGET_SDL
759   XFlush(display);
760 #endif
761 }
762
763 /* execute and wait for all pending screen drawing operations */
764 void SyncDisplay(void)
765 {
766 #ifndef TARGET_SDL
767   XSync(display, FALSE);
768 #endif
769 }
770
771 void KeyboardAutoRepeatOn(void)
772 {
773 #if defined(TARGET_SDL)
774   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
775                       SDL_DEFAULT_REPEAT_INTERVAL / 2);
776   SDL_EnableUNICODE(1);
777 #else
778   if (display)
779     XAutoRepeatOn(display);
780 #endif
781 }
782
783 void KeyboardAutoRepeatOff(void)
784 {
785 #if defined(TARGET_SDL)
786   SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
787   SDL_EnableUNICODE(0);
788 #else
789   if (display)
790     XAutoRepeatOff(display);
791 #endif
792 }
793
794 boolean PointerInWindow(DrawWindow *window)
795 {
796 #if defined(TARGET_SDL)
797   return TRUE;
798 #else
799   Window root, child;
800   int root_x, root_y;
801   unsigned int mask;
802   int win_x, win_y;
803
804   /* if XQueryPointer() returns False, the pointer
805      is not on the same screen as the specified window */
806   return XQueryPointer(display, window->drawable, &root, &child,
807                        &root_x, &root_y, &win_x, &win_y, &mask);
808 #endif
809 }
810
811 boolean SetVideoMode(boolean fullscreen)
812 {
813 #if defined(TARGET_SDL)
814   return SDLSetVideoMode(&backbuffer, fullscreen);
815 #else
816   boolean success = TRUE;
817
818   if (fullscreen && video.fullscreen_available)
819   {
820     Error(ERR_WARN, "fullscreen not available in X11 version");
821
822     /* display error message only once */
823     video.fullscreen_available = FALSE;
824
825     success = FALSE;
826   }
827
828   return success;
829 #endif
830 }
831
832 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
833 {
834 #if defined(TARGET_SDL)
835   if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
836       (!fullscreen && video.fullscreen_enabled))
837     fullscreen = SetVideoMode(fullscreen);
838 #endif
839
840   return fullscreen;
841 }
842
843 Bitmap *LoadImage(char *filename)
844 {
845   Bitmap *new_bitmap;
846
847 #if defined(TARGET_SDL)
848   new_bitmap = SDLLoadImage(filename);
849 #else
850   new_bitmap = X11LoadImage(filename);
851 #endif
852
853   if (new_bitmap)
854     new_bitmap->source_filename = getStringCopy(filename);
855
856   return new_bitmap;
857 }
858
859 Bitmap *LoadCustomImage(char *basename)
860 {
861   char *filename = getCustomImageFilename(basename);
862   Bitmap *new_bitmap;
863
864   if (filename == NULL)
865     Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
866
867   if ((new_bitmap = LoadImage(filename)) == NULL)
868     Error(ERR_EXIT, "LoadImage() failed: %s", GetError());
869
870   return new_bitmap;
871 }
872
873 void ReloadCustomImage(Bitmap *bitmap, char *basename)
874 {
875   char *filename = getCustomImageFilename(basename);
876   Bitmap *new_bitmap;
877
878   if (filename == NULL)         /* (should never happen) */
879   {
880     Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
881     return;
882   }
883
884   if (strEqual(filename, bitmap->source_filename))
885   {
886     /* The old and new image are the same (have the same filename and path).
887        This usually means that this image does not exist in this graphic set
888        and a fallback to the existing image is done. */
889
890     return;
891   }
892
893   if ((new_bitmap = LoadImage(filename)) == NULL)
894   {
895     Error(ERR_WARN, "LoadImage() failed: %s", GetError());
896     return;
897   }
898
899   if (bitmap->width != new_bitmap->width ||
900       bitmap->height != new_bitmap->height)
901   {
902     Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
903           filename);
904     FreeBitmap(new_bitmap);
905     return;
906   }
907
908   TransferBitmapPointers(new_bitmap, bitmap);
909   free(new_bitmap);
910 }
911
912 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
913 {
914   Bitmap *dst_bitmap = CreateBitmap(zoom_width, zoom_height, DEFAULT_DEPTH);
915
916 #if defined(TARGET_SDL)
917   SDLZoomBitmap(src_bitmap, dst_bitmap);
918 #else
919   X11ZoomBitmap(src_bitmap, dst_bitmap);
920 #endif
921
922   return dst_bitmap;
923 }
924
925 static void CreateScaledBitmaps(Bitmap *old_bitmap, int zoom_factor,
926                                 boolean create_small_bitmaps)
927 {
928   Bitmap swap_bitmap;
929   Bitmap *new_bitmap;
930   Bitmap *tmp_bitmap_1;
931   Bitmap *tmp_bitmap_2;
932   Bitmap *tmp_bitmap_4;
933   Bitmap *tmp_bitmap_8;
934   Bitmap *tmp_bitmap_16;
935   Bitmap *tmp_bitmap_32;
936   int width_1, height_1;
937   int width_2, height_2;
938   int width_4, height_4;
939   int width_8, height_8;
940   int width_16, height_16;
941   int width_32, height_32;
942   int new_width, new_height;
943
944   /* calculate new image dimensions for normal sized image */
945   width_1  = old_bitmap->width  * zoom_factor;
946   height_1 = old_bitmap->height * zoom_factor;
947
948   /* get image with normal size (this might require scaling up) */
949   if (zoom_factor != 1)
950     tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
951   else
952     tmp_bitmap_1 = old_bitmap;
953
954   /* this is only needed to make compilers happy */
955   tmp_bitmap_2 = NULL;
956   tmp_bitmap_4 = NULL;
957   tmp_bitmap_8 = NULL;
958   tmp_bitmap_16 = NULL;
959   tmp_bitmap_32 = NULL;
960
961   if (create_small_bitmaps)
962   {
963     /* calculate new image dimensions for small images */
964     width_2  = width_1  / 2;
965     height_2 = height_1 / 2;
966     width_4  = width_1  / 4;
967     height_4 = height_1 / 4;
968     width_8  = width_1  / 8;
969     height_8 = height_1 / 8;
970     width_16  = width_1  / 16;
971     height_16 = height_1 / 16;
972     width_32  = width_1  / 32;
973     height_32 = height_1 / 32;
974
975     UPDATE_BUSY_STATE();
976
977     /* get image with 1/2 of normal size (for use in the level editor) */
978     if (zoom_factor != 2)
979       tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_1 / 2, height_1 / 2);
980     else
981       tmp_bitmap_2 = old_bitmap;
982
983     UPDATE_BUSY_STATE();
984
985     /* get image with 1/4 of normal size (for use in the level editor) */
986     if (zoom_factor != 4)
987       tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_2 / 2, height_2 / 2);
988     else
989       tmp_bitmap_4 = old_bitmap;
990
991     UPDATE_BUSY_STATE();
992
993     /* get image with 1/8 of normal size (for use on the preview screen) */
994     if (zoom_factor != 8)
995       tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_4 / 2, height_4 / 2);
996     else
997       tmp_bitmap_8 = old_bitmap;
998
999     UPDATE_BUSY_STATE();
1000
1001     /* get image with 1/16 of normal size (for use on the preview screen) */
1002     if (zoom_factor != 16)
1003       tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_8 / 2, height_8 / 2);
1004     else
1005       tmp_bitmap_16 = old_bitmap;
1006
1007     UPDATE_BUSY_STATE();
1008
1009     /* get image with 1/32 of normal size (for use on the preview screen) */
1010     if (zoom_factor != 32)
1011       tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_16 / 2, height_16 / 2);
1012     else
1013       tmp_bitmap_32 = old_bitmap;
1014
1015     UPDATE_BUSY_STATE();
1016   }
1017
1018 #if 0
1019   /* if image was scaled up, create new clipmask for normal size image */
1020   if (zoom_factor != 1)
1021   {
1022 #if defined(TARGET_X11)
1023     if (old_bitmap->clip_mask)
1024       XFreePixmap(display, old_bitmap->clip_mask);
1025
1026     old_bitmap->clip_mask =
1027       Pixmap_to_Mask(tmp_bitmap_1->drawable, width_1, height_1);
1028
1029     XSetClipMask(display, old_bitmap->stored_clip_gc, old_bitmap->clip_mask);
1030 #else
1031     SDL_Surface *tmp_surface_1 = tmp_bitmap_1->surface;
1032
1033     if (old_bitmap->surface_masked)
1034       SDL_FreeSurface(old_bitmap->surface_masked);
1035
1036     SDL_SetColorKey(tmp_surface_1, SDL_SRCCOLORKEY,
1037                     SDL_MapRGB(tmp_surface_1->format, 0x00, 0x00, 0x00));
1038     if ((old_bitmap->surface_masked = SDL_DisplayFormat(tmp_surface_1)) ==NULL)
1039       Error(ERR_EXIT, "SDL_DisplayFormat() failed");
1040     SDL_SetColorKey(tmp_surface_1, 0, 0);       /* reset transparent pixel */
1041 #endif
1042   }
1043 #endif
1044
1045   if (create_small_bitmaps)
1046   {
1047     new_width  = width_1;
1048     new_height = height_1 + (height_1 + 1) / 2;     /* prevent odd height */
1049
1050     new_bitmap = CreateBitmap(new_width, new_height, DEFAULT_DEPTH);
1051
1052     BlitBitmap(tmp_bitmap_1, new_bitmap, 0, 0, width_1, height_1, 0, 0);
1053     BlitBitmap(tmp_bitmap_2, new_bitmap, 0, 0, width_1 / 2, height_1 / 2,
1054                0, height_1);
1055     BlitBitmap(tmp_bitmap_4, new_bitmap, 0, 0, width_1 / 4, height_1 / 4,
1056                width_1 / 2, height_1);
1057     BlitBitmap(tmp_bitmap_8, new_bitmap, 0, 0, width_1 / 8, height_1 / 8,
1058                3 * width_1 / 4, height_1);
1059     BlitBitmap(tmp_bitmap_16, new_bitmap, 0, 0, width_1 / 16, height_1 / 16,
1060                7 * width_1 / 8, height_1);
1061     BlitBitmap(tmp_bitmap_32, new_bitmap, 0, 0, width_1 / 32, height_1 / 32,
1062                15 * width_1 / 16, height_1);
1063
1064     UPDATE_BUSY_STATE();
1065   }
1066   else
1067   {
1068     new_width  = width_1;
1069     new_height = height_1;
1070
1071     new_bitmap = tmp_bitmap_1;  /* directly use tmp_bitmap_1 as new bitmap */
1072   }
1073
1074   if (create_small_bitmaps)
1075   {
1076     /* if no small bitmaps created, tmp_bitmap_1 is used as new bitmap now */
1077     if (zoom_factor != 1)
1078       FreeBitmap(tmp_bitmap_1);
1079
1080     if (zoom_factor != 2)
1081       FreeBitmap(tmp_bitmap_2);
1082
1083     if (zoom_factor != 4)
1084       FreeBitmap(tmp_bitmap_4);
1085
1086     if (zoom_factor != 8)
1087       FreeBitmap(tmp_bitmap_8);
1088
1089     if (zoom_factor != 16)
1090       FreeBitmap(tmp_bitmap_16);
1091
1092     if (zoom_factor != 32)
1093       FreeBitmap(tmp_bitmap_32);
1094   }
1095
1096   /* replace image with extended image (containing 1/1, 1/2, 1/4, 1/8 size) */
1097 #if defined(TARGET_SDL)
1098   swap_bitmap.surface = old_bitmap->surface;
1099   old_bitmap->surface = new_bitmap->surface;
1100   new_bitmap->surface = swap_bitmap.surface;
1101 #else
1102   swap_bitmap.drawable = old_bitmap->drawable;
1103   old_bitmap->drawable = new_bitmap->drawable;
1104   new_bitmap->drawable = swap_bitmap.drawable;
1105 #endif
1106
1107   old_bitmap->width  = new_bitmap->width;
1108   old_bitmap->height = new_bitmap->height;
1109
1110 #if 1
1111   /* this replaces all blit masks created when loading -- maybe optimize this */
1112   {
1113 #if defined(TARGET_X11)
1114     if (old_bitmap->clip_mask)
1115       XFreePixmap(display, old_bitmap->clip_mask);
1116
1117     old_bitmap->clip_mask =
1118       Pixmap_to_Mask(old_bitmap->drawable, new_width, new_height);
1119
1120     XSetClipMask(display, old_bitmap->stored_clip_gc, old_bitmap->clip_mask);
1121 #else
1122     SDL_Surface *old_surface = old_bitmap->surface;
1123
1124     if (old_bitmap->surface_masked)
1125       SDL_FreeSurface(old_bitmap->surface_masked);
1126
1127     SDL_SetColorKey(old_surface, SDL_SRCCOLORKEY,
1128                     SDL_MapRGB(old_surface->format, 0x00, 0x00, 0x00));
1129     if ((old_bitmap->surface_masked = SDL_DisplayFormat(old_surface)) ==NULL)
1130       Error(ERR_EXIT, "SDL_DisplayFormat() failed");
1131     SDL_SetColorKey(old_surface, 0, 0);         /* reset transparent pixel */
1132 #endif
1133   }
1134 #endif
1135
1136   UPDATE_BUSY_STATE();
1137
1138   FreeBitmap(new_bitmap);       /* this actually frees the _old_ bitmap now */
1139 }
1140
1141 void CreateBitmapWithSmallBitmaps(Bitmap *old_bitmap, int zoom_factor)
1142 {
1143   CreateScaledBitmaps(old_bitmap, zoom_factor, TRUE);
1144 }
1145
1146 void ScaleBitmap(Bitmap *old_bitmap, int zoom_factor)
1147 {
1148   CreateScaledBitmaps(old_bitmap, zoom_factor, FALSE);
1149 }
1150
1151
1152 /* ------------------------------------------------------------------------- */
1153 /* mouse pointer functions                                                   */
1154 /* ------------------------------------------------------------------------- */
1155
1156 #if !defined(PLATFORM_MSDOS)
1157 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER            0
1158 /* XPM image definitions */
1159 static const char *cursor_image_none[] =
1160 {
1161   /* width height num_colors chars_per_pixel */
1162   "    16    16        3            1",
1163
1164   /* colors */
1165   "X c #000000",
1166   ". c #ffffff",
1167   "  c None",
1168
1169   /* pixels */
1170   "                ",
1171   "                ",
1172   "                ",
1173   "                ",
1174   "                ",
1175   "                ",
1176   "                ",
1177   "                ",
1178   "                ",
1179   "                ",
1180   "                ",
1181   "                ",
1182   "                ",
1183   "                ",
1184   "                ",
1185   "                ",
1186
1187   /* hot spot */
1188   "0,0"
1189 };
1190 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1191 static const char *cursor_image_dot[] =
1192 {
1193   /* width height num_colors chars_per_pixel */
1194   "    16    16        3            1",
1195
1196   /* colors */
1197   "X c #000000",
1198   ". c #ffffff",
1199   "  c None",
1200
1201   /* pixels */
1202   " X              ",
1203   "X.X             ",
1204   " X              ",
1205   "                ",
1206   "                ",
1207   "                ",
1208   "                ",
1209   "                ",
1210   "                ",
1211   "                ",
1212   "                ",
1213   "                ",
1214   "                ",
1215   "                ",
1216   "                ",
1217   "                ",
1218
1219   /* hot spot */
1220   "1,1"
1221 };
1222 static const char **cursor_image_playfield = cursor_image_dot;
1223 #else
1224 /* some people complained about a "white dot" on the screen and thought it
1225    was a graphical error... OK, let's just remove the whole pointer :-) */
1226 static const char **cursor_image_playfield = cursor_image_none;
1227 #endif
1228
1229 #if defined(TARGET_SDL)
1230 static const int cursor_bit_order = BIT_ORDER_MSB;
1231 #elif defined(TARGET_X11_NATIVE)
1232 static const int cursor_bit_order = BIT_ORDER_LSB;
1233 #endif
1234
1235 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1236 {
1237   struct MouseCursorInfo *cursor;
1238   boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1239   int header_lines = 4;
1240   int x, y, i;
1241
1242   cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1243
1244   sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1245
1246   i = -1;
1247   for (y = 0; y < cursor->width; y++)
1248   {
1249     for (x = 0; x < cursor->height; x++)
1250     {
1251       int bit_nr = x % 8;
1252       int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1253
1254       if (bit_nr == 0)
1255       {
1256         i++;
1257         cursor->data[i] = cursor->mask[i] = 0;
1258       }
1259
1260       switch (image[header_lines + y][x])
1261       {
1262         case 'X':
1263           cursor->data[i] |= bit_mask;
1264           cursor->mask[i] |= bit_mask;
1265           break;
1266
1267         case '.':
1268           cursor->mask[i] |= bit_mask;
1269           break;
1270
1271         case ' ':
1272           break;
1273       }
1274     }
1275   }
1276
1277   sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1278
1279   return cursor;
1280 }
1281 #endif  /* !PLATFORM_MSDOS */
1282
1283 void SetMouseCursor(int mode)
1284 {
1285 #if !defined(PLATFORM_MSDOS)
1286   static struct MouseCursorInfo *cursor_none = NULL;
1287   static struct MouseCursorInfo *cursor_playfield = NULL;
1288   struct MouseCursorInfo *cursor_new;
1289
1290   if (cursor_none == NULL)
1291     cursor_none = get_cursor_from_image(cursor_image_none);
1292
1293   if (cursor_playfield == NULL)
1294     cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1295
1296   cursor_new = (mode == CURSOR_DEFAULT   ? NULL :
1297                 mode == CURSOR_NONE      ? cursor_none :
1298                 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1299
1300 #if defined(TARGET_SDL)
1301   SDLSetMouseCursor(cursor_new);
1302 #elif defined(TARGET_X11_NATIVE)
1303   X11SetMouseCursor(cursor_new);
1304 #endif
1305 #endif
1306 }
1307
1308
1309 /* ========================================================================= */
1310 /* audio functions                                                           */
1311 /* ========================================================================= */
1312
1313 void OpenAudio(void)
1314 {
1315   /* always start with reliable default values */
1316   audio.sound_available = FALSE;
1317   audio.music_available = FALSE;
1318   audio.loops_available = FALSE;
1319
1320   audio.sound_enabled = FALSE;
1321   audio.sound_deactivated = FALSE;
1322
1323   audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1324   audio.mixer_pid = 0;
1325   audio.device_name = NULL;
1326   audio.device_fd = -1;
1327
1328   audio.num_channels = 0;
1329   audio.music_channel = 0;
1330   audio.first_sound_channel = 0;
1331
1332 #if defined(TARGET_SDL)
1333   SDLOpenAudio();
1334 #elif defined(PLATFORM_UNIX)
1335   UnixOpenAudio();
1336 #elif defined(PLATFORM_MSDOS)
1337   MSDOSOpenAudio();
1338 #endif
1339 }
1340
1341 void CloseAudio(void)
1342 {
1343 #if defined(TARGET_SDL)
1344   SDLCloseAudio();
1345 #elif defined(PLATFORM_UNIX)
1346   UnixCloseAudio();
1347 #elif defined(PLATFORM_MSDOS)
1348   MSDOSCloseAudio();
1349 #endif
1350
1351   audio.sound_enabled = FALSE;
1352 }
1353
1354 void SetAudioMode(boolean enabled)
1355 {
1356   if (!audio.sound_available)
1357     return;
1358
1359   audio.sound_enabled = enabled;
1360 }
1361
1362
1363 /* ========================================================================= */
1364 /* event functions                                                           */
1365 /* ========================================================================= */
1366
1367 void InitEventFilter(EventFilter filter_function)
1368 {
1369 #if defined(TARGET_SDL)
1370   /* set event filter to filter out certain events */
1371   SDL_SetEventFilter(filter_function);
1372 #endif
1373 }
1374
1375 boolean PendingEvent(void)
1376 {
1377 #if defined(TARGET_SDL)
1378   return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1379 #else
1380   return (XPending(display) ? TRUE : FALSE);
1381 #endif
1382 }
1383
1384 void NextEvent(Event *event)
1385 {
1386 #if defined(TARGET_SDL)
1387   SDLNextEvent(event);
1388 #else
1389   XNextEvent(display, event);
1390 #endif
1391 }
1392
1393 void PeekEvent(Event *event)
1394 {
1395 #if defined(TARGET_SDL)
1396   SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1397 #else
1398   XPeekEvent(display, event);
1399 #endif
1400 }
1401
1402 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1403 {
1404 #if defined(TARGET_SDL)
1405
1406 #if 0
1407   printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1408          (int)event->keysym.unicode,
1409          (int)event->keysym.sym,
1410          (int)SDL_GetModState());
1411 #endif
1412
1413   if (with_modifiers &&
1414       event->keysym.unicode > 0x0000 &&
1415       event->keysym.unicode < 0x2000)
1416     return event->keysym.unicode;
1417   else
1418     return event->keysym.sym;
1419
1420 #else
1421
1422 #if 0
1423   printf("with modifiers == '0x%04x', without modifiers == '0x%04x'\n",
1424          (int)XLookupKeysym(event, event->state),
1425          (int)XLookupKeysym(event, 0));
1426 #endif
1427
1428   if (with_modifiers)
1429     return XLookupKeysym(event, event->state);
1430   else
1431     return XLookupKeysym(event, 0);
1432 #endif
1433 }
1434
1435 KeyMod HandleKeyModState(Key key, int key_status)
1436 {
1437   static KeyMod current_modifiers = KMOD_None;
1438
1439   if (key != KSYM_UNDEFINED)    /* new key => check for modifier key change */
1440   {
1441     KeyMod new_modifier = KMOD_None;
1442
1443     switch(key)
1444     {
1445       case KSYM_Shift_L:
1446         new_modifier = KMOD_Shift_L;
1447         break;
1448       case KSYM_Shift_R:
1449         new_modifier = KMOD_Shift_R;
1450         break;
1451       case KSYM_Control_L:
1452         new_modifier = KMOD_Control_L;
1453         break;
1454       case KSYM_Control_R:
1455         new_modifier = KMOD_Control_R;
1456         break;
1457       case KSYM_Meta_L:
1458         new_modifier = KMOD_Meta_L;
1459         break;
1460       case KSYM_Meta_R:
1461         new_modifier = KMOD_Meta_R;
1462         break;
1463       case KSYM_Alt_L:
1464         new_modifier = KMOD_Alt_L;
1465         break;
1466       case KSYM_Alt_R:
1467         new_modifier = KMOD_Alt_R;
1468         break;
1469       default:
1470         break;
1471     }
1472
1473     if (key_status == KEY_PRESSED)
1474       current_modifiers |= new_modifier;
1475     else
1476       current_modifiers &= ~new_modifier;
1477   }
1478
1479   return current_modifiers;
1480 }
1481
1482 KeyMod GetKeyModState()
1483 {
1484 #if defined(TARGET_SDL)
1485   return (KeyMod)SDL_GetModState();
1486 #else
1487   return HandleKeyModState(KSYM_UNDEFINED, 0);
1488 #endif
1489 }
1490
1491 KeyMod GetKeyModStateFromEvents()
1492 {
1493   /* always use key modifier state as tracked from key events (this is needed
1494      if the modifier key event was injected into the event queue, but the key
1495      was not really pressed on keyboard -- SDL_GetModState() seems to directly
1496      query the keys as held pressed on the keyboard) -- this case is currently
1497      only used to filter out clipboard insert events from "True X-Mouse" tool */
1498
1499   return HandleKeyModState(KSYM_UNDEFINED, 0);
1500 }
1501
1502 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1503 {
1504   if (event->type != EVENT_CLIENTMESSAGE)
1505     return FALSE;
1506
1507 #if defined(TARGET_SDL)
1508   return TRUE;          /* the only possible message here is SDL_QUIT */
1509 #elif defined(PLATFORM_UNIX)
1510   if ((event->window == window->drawable) &&
1511       (event->data.l[0] == XInternAtom(display, "WM_DELETE_WINDOW", FALSE)))
1512     return TRUE;
1513 #endif
1514
1515   return FALSE;
1516 }
1517
1518
1519 /* ========================================================================= */
1520 /* joystick functions                                                        */
1521 /* ========================================================================= */
1522
1523 void InitJoysticks()
1524 {
1525   int i;
1526
1527 #if defined(NO_JOYSTICK)
1528   return;       /* joysticks generally deactivated by compile-time directive */
1529 #endif
1530
1531   /* always start with reliable default values */
1532   joystick.status = JOYSTICK_NOT_AVAILABLE;
1533   for (i = 0; i < MAX_PLAYERS; i++)
1534     joystick.fd[i] = -1;                /* joystick device closed */
1535
1536 #if defined(TARGET_SDL)
1537   SDLInitJoysticks();
1538 #elif defined(PLATFORM_UNIX)
1539   UnixInitJoysticks();
1540 #elif defined(PLATFORM_MSDOS)
1541   MSDOSInitJoysticks();
1542 #endif
1543
1544 #if 0
1545   for (i = 0; i < MAX_PLAYERS; i++)
1546     printf("::: Joystick for player %d: %d\n", i, joystick.fd[i]);
1547 #endif
1548 }
1549
1550 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1551 {
1552 #if defined(TARGET_SDL)
1553   return SDLReadJoystick(nr, x, y, b1, b2);
1554 #elif defined(PLATFORM_UNIX)
1555   return UnixReadJoystick(nr, x, y, b1, b2);
1556 #elif defined(PLATFORM_MSDOS)
1557   return MSDOSReadJoystick(nr, x, y, b1, b2);
1558 #endif
1559 }