fixed re-initialization of textures after change of renderer
[rocksndiamonds.git] / src / libgame / system.c
1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // system.c
10 // ============================================================================
11
12 #include <string.h>
13 #include <signal.h>
14
15 #include "platform.h"
16
17 #include "system.h"
18 #include "image.h"
19 #include "sound.h"
20 #include "setup.h"
21 #include "joystick.h"
22 #include "misc.h"
23
24 #define ENABLE_UNUSED_CODE      0       /* currently unused functions */
25
26
27 /* ========================================================================= */
28 /* exported variables                                                        */
29 /* ========================================================================= */
30
31 struct ProgramInfo      program;
32 struct OptionInfo       options;
33 struct VideoSystemInfo  video;
34 struct AudioSystemInfo  audio;
35 struct GfxInfo          gfx;
36 struct ArtworkInfo      artwork;
37 struct JoystickInfo     joystick;
38 struct SetupInfo        setup;
39
40 LevelDirTree           *leveldir_first_all = NULL;
41 LevelDirTree           *leveldir_first = NULL;
42 LevelDirTree           *leveldir_current = NULL;
43 int                     level_nr;
44
45 struct LevelStats       level_stats[MAX_LEVELS];
46
47 DrawWindow             *window = NULL;
48 DrawBuffer             *backbuffer = NULL;
49 DrawBuffer             *drawto = NULL;
50
51 int                     button_status = MB_NOT_PRESSED;
52 boolean                 motion_status = FALSE;
53 #if defined(TARGET_SDL2)
54 boolean                 keyrepeat_status = TRUE;
55 #endif
56
57 int                     redraw_mask = REDRAW_NONE;
58
59 int                     FrameCounter = 0;
60
61
62 /* ========================================================================= */
63 /* init/close functions                                                      */
64 /* ========================================================================= */
65
66 void InitProgramInfo(char *argv0, char *config_filename, char *userdata_subdir,
67                      char *program_title, char *icon_title,
68                      char *icon_filename, char *cookie_prefix,
69                      int program_version)
70 {
71   program.command_basepath = getBasePath(argv0);
72   program.command_basename = getBaseName(argv0);
73
74   program.config_filename = config_filename;
75
76   program.userdata_subdir = userdata_subdir;
77   program.userdata_path = getUserGameDataDir();
78
79   program.program_title = program_title;
80   program.window_title = "(undefined)";
81   program.icon_title = icon_title;
82
83   program.icon_filename = icon_filename;
84
85   program.cookie_prefix = cookie_prefix;
86
87   program.version_major = VERSION_MAJOR(program_version);
88   program.version_minor = VERSION_MINOR(program_version);
89   program.version_patch = VERSION_PATCH(program_version);
90   program.version_build = VERSION_BUILD(program_version);
91   program.version_ident = program_version;
92
93   program.log_filename[LOG_OUT_ID] = getLogFilename(LOG_OUT_BASENAME);
94   program.log_filename[LOG_ERR_ID] = getLogFilename(LOG_ERR_BASENAME);
95   program.log_file[LOG_OUT_ID] = program.log_file_default[LOG_OUT_ID] = stdout;
96   program.log_file[LOG_ERR_ID] = program.log_file_default[LOG_ERR_ID] = stderr;
97 }
98
99 void SetWindowTitle()
100 {
101   program.window_title = program.window_title_function();
102
103   SDLSetWindowTitle();
104 }
105
106 void InitWindowTitleFunction(char *(*window_title_function)(void))
107 {
108   program.window_title_function = window_title_function;
109 }
110
111 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
112 {
113   program.exit_message_function = exit_message_function;
114 }
115
116 void InitExitFunction(void (*exit_function)(int))
117 {
118   program.exit_function = exit_function;
119
120   /* set signal handlers to custom exit function */
121   // signal(SIGINT, exit_function);
122   signal(SIGTERM, exit_function);
123
124   /* set exit function to automatically cleanup SDL stuff after exit() */
125   atexit(SDL_Quit);
126 }
127
128 void InitPlatformDependentStuff(void)
129 {
130   // this is initialized in GetOptions(), but may already be used before
131   options.verbose = TRUE;
132
133   OpenLogFiles();
134
135 #if defined(TARGET_SDL2)
136   int sdl_init_flags = SDL_INIT_EVENTS      | SDL_INIT_NOPARACHUTE;
137 #else
138   int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
139 #endif
140
141   if (SDL_Init(sdl_init_flags) < 0)
142     Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
143
144   SDLNet_Init();
145 }
146
147 void ClosePlatformDependentStuff(void)
148 {
149   CloseLogFiles();
150 }
151
152 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
153                       int real_sx, int real_sy,
154                       int full_sxsize, int full_sysize,
155                       Bitmap *field_save_buffer)
156 {
157   gfx.sx = sx;
158   gfx.sy = sy;
159   gfx.sxsize = sxsize;
160   gfx.sysize = sysize;
161   gfx.real_sx = real_sx;
162   gfx.real_sy = real_sy;
163   gfx.full_sxsize = full_sxsize;
164   gfx.full_sysize = full_sysize;
165
166   gfx.field_save_buffer = field_save_buffer;
167
168   SetDrawDeactivationMask(REDRAW_NONE);         /* do not deactivate drawing */
169   SetDrawBackgroundMask(REDRAW_NONE);           /* deactivate masked drawing */
170 }
171
172 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
173 {
174   gfx.game_tile_size = game_tile_size;
175   gfx.standard_tile_size = standard_tile_size;
176 }
177
178 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
179 {
180   gfx.dx = dx;
181   gfx.dy = dy;
182   gfx.dxsize = dxsize;
183   gfx.dysize = dysize;
184 }
185
186 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
187 {
188   gfx.vx = vx;
189   gfx.vy = vy;
190   gfx.vxsize = vxsize;
191   gfx.vysize = vysize;
192 }
193
194 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
195 {
196   gfx.ex = ex;
197   gfx.ey = ey;
198   gfx.exsize = exsize;
199   gfx.eysize = eysize;
200 }
201
202 void InitGfxWindowInfo(int win_xsize, int win_ysize)
203 {
204   gfx.win_xsize = win_xsize;
205   gfx.win_ysize = win_ysize;
206
207   gfx.background_bitmap_mask = REDRAW_NONE;
208
209   ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize, DEFAULT_DEPTH);
210 #if USE_FINAL_SCREEN_BITMAP
211   ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize, DEFAULT_DEPTH);
212 #endif
213 }
214
215 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
216 {
217   /* currently only used by MSDOS code to alloc VRAM buffer, if available */
218   /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
219   gfx.scrollbuffer_width = scrollbuffer_width;
220   gfx.scrollbuffer_height = scrollbuffer_height;
221 }
222
223 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
224 {
225   gfx.clipping_enabled = enabled;
226   gfx.clip_x = x;
227   gfx.clip_y = y;
228   gfx.clip_width = width;
229   gfx.clip_height = height;
230 }
231
232 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
233 {
234   gfx.draw_busy_anim_function = draw_busy_anim_function;
235 }
236
237 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(void))
238 {
239   gfx.draw_global_anim_function = draw_global_anim_function;
240 }
241
242 void InitGfxCustomArtworkInfo()
243 {
244   gfx.override_level_graphics = FALSE;
245   gfx.override_level_sounds = FALSE;
246   gfx.override_level_music = FALSE;
247
248   gfx.draw_init_text = TRUE;
249 }
250
251 void InitGfxOtherSettings()
252 {
253   gfx.cursor_mode = CURSOR_DEFAULT;
254 }
255
256 void SetDrawDeactivationMask(int draw_deactivation_mask)
257 {
258   gfx.draw_deactivation_mask = draw_deactivation_mask;
259 }
260
261 void SetDrawBackgroundMask(int draw_background_mask)
262 {
263   gfx.draw_background_mask = draw_background_mask;
264 }
265
266 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
267 {
268   if (background_bitmap_tile != NULL)
269     gfx.background_bitmap_mask |= mask;
270   else
271     gfx.background_bitmap_mask &= ~mask;
272
273   if (background_bitmap_tile == NULL)   /* empty background requested */
274     return;
275
276   if (mask == REDRAW_ALL)
277     BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
278                     0, 0, video.width, video.height);
279   else if (mask == REDRAW_FIELD)
280     BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
281                     gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
282   else if (mask == REDRAW_DOOR_1)
283     BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
284                     gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
285 }
286
287 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
288 {
289   /* remove every mask before setting mask for window */
290   /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
291   SetBackgroundBitmap(NULL, 0xffff);            /* !!! FIX THIS !!! */
292   SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
293 }
294
295 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
296 {
297   /* remove window area mask before setting mask for main area */
298   /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
299   SetBackgroundBitmap(NULL, REDRAW_ALL);        /* !!! FIX THIS !!! */
300   SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
301 }
302
303 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
304 {
305   /* remove window area mask before setting mask for door area */
306   /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
307   SetBackgroundBitmap(NULL, REDRAW_ALL);        /* !!! FIX THIS !!! */
308   SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
309 }
310
311
312 /* ========================================================================= */
313 /* video functions                                                           */
314 /* ========================================================================= */
315
316 inline static int GetRealDepth(int depth)
317 {
318   return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
319 }
320
321 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
322                                     int width, int height, Pixel color)
323 {
324   SDLFillRectangle(bitmap, x, y, width, height, color);
325
326   if (bitmap == backbuffer)
327     SetRedrawMaskFromArea(x, y, width, height);
328 }
329
330 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
331                                int src_x, int src_y, int width, int height,
332                                int dst_x, int dst_y, int mask_mode)
333 {
334   SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
335               dst_x, dst_y, mask_mode);
336
337   if (dst_bitmap == backbuffer)
338     SetRedrawMaskFromArea(dst_x, dst_y, width, height);
339 }
340
341 void LimitScreenUpdates(boolean enable)
342 {
343   SDLLimitScreenUpdates(enable);
344 }
345
346 void InitVideoDisplay(void)
347 {
348   SDLInitVideoDisplay();
349 }
350
351 void CloseVideoDisplay(void)
352 {
353   KeyboardAutoRepeatOn();
354
355   SDL_QuitSubSystem(SDL_INIT_VIDEO);
356 }
357
358 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
359 {
360   video.width = width;
361   video.height = height;
362   video.depth = GetRealDepth(depth);
363
364   video.fullscreen_available = FULLSCREEN_STATUS;
365   video.fullscreen_enabled = FALSE;
366
367   video.window_scaling_available = WINDOW_SCALING_STATUS;
368
369   SDLInitVideoBuffer(&backbuffer, &window, fullscreen);
370
371   video.initialized = TRUE;
372
373   drawto = backbuffer;
374 }
375
376 inline static void FreeBitmapPointers(Bitmap *bitmap)
377 {
378   if (bitmap == NULL)
379     return;
380
381   SDLFreeBitmapPointers(bitmap);
382
383   checked_free(bitmap->source_filename);
384   bitmap->source_filename = NULL;
385 }
386
387 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
388                                           Bitmap *dst_bitmap)
389 {
390   if (src_bitmap == NULL || dst_bitmap == NULL)
391     return;
392
393   FreeBitmapPointers(dst_bitmap);
394
395   *dst_bitmap = *src_bitmap;
396 }
397
398 void FreeBitmap(Bitmap *bitmap)
399 {
400   if (bitmap == NULL)
401     return;
402
403   FreeBitmapPointers(bitmap);
404
405   free(bitmap);
406 }
407
408 Bitmap *CreateBitmapStruct(void)
409 {
410   return checked_calloc(sizeof(Bitmap));
411 }
412
413 Bitmap *CreateBitmap(int width, int height, int depth)
414 {
415   Bitmap *new_bitmap = CreateBitmapStruct();
416   int real_width  = MAX(1, width);      /* prevent zero bitmap width */
417   int real_height = MAX(1, height);     /* prevent zero bitmap height */
418   int real_depth  = GetRealDepth(depth);
419
420   SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
421
422   new_bitmap->width  = real_width;
423   new_bitmap->height = real_height;
424
425   return new_bitmap;
426 }
427
428 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
429 {
430   Bitmap *new_bitmap = CreateBitmap(width, height, depth);
431
432   if (*bitmap == NULL)
433   {
434     *bitmap = new_bitmap;
435   }
436   else
437   {
438     TransferBitmapPointers(new_bitmap, *bitmap);
439     free(new_bitmap);
440   }
441 }
442
443 void CloseWindow(DrawWindow *window)
444 {
445 }
446
447 void SetRedrawMaskFromArea(int x, int y, int width, int height)
448 {
449   int x1 = x;
450   int y1 = y;
451   int x2 = x + width - 1;
452   int y2 = y + height - 1;
453
454   if (width == 0 || height == 0)
455     return;
456
457   if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
458     redraw_mask |= REDRAW_FIELD;
459   else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
460     redraw_mask |= REDRAW_DOOR_1;
461   else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
462     redraw_mask |= REDRAW_DOOR_2;
463   else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
464     redraw_mask |= REDRAW_DOOR_3;
465   else
466     redraw_mask = REDRAW_ALL;
467 }
468
469 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
470                                        int draw_mask)
471 {
472   if (draw_mask == REDRAW_NONE)
473     return FALSE;
474
475   if (draw_mask & REDRAW_ALL)
476     return TRUE;
477
478   if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
479     return TRUE;
480
481   if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
482     return TRUE;
483
484   if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
485     return TRUE;
486
487   if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
488     return TRUE;
489
490   return FALSE;
491 }
492
493 boolean DrawingDeactivated(int x, int y, int width, int height)
494 {
495   return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
496 }
497
498 boolean DrawingOnBackground(int x, int y)
499 {
500   return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
501           CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
502 }
503
504 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
505                                   int *width, int *height, boolean is_dest)
506 {
507   int clip_x, clip_y, clip_width, clip_height;
508
509   if (gfx.clipping_enabled && is_dest)  /* only clip destination bitmap */
510   {
511     clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
512     clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
513     clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
514     clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
515   }
516   else
517   {
518     clip_x = 0;
519     clip_y = 0;
520     clip_width = bitmap->width;
521     clip_height = bitmap->height;
522   }
523
524   /* skip if rectangle completely outside bitmap */
525
526   if (*x + *width  <= clip_x ||
527       *y + *height <= clip_y ||
528       *x >= clip_x + clip_width ||
529       *y >= clip_y + clip_height)
530     return FALSE;
531
532   /* clip if rectangle overlaps bitmap */
533
534   if (*x < clip_x)
535   {
536     *width -= clip_x - *x;
537     *x = clip_x;
538   }
539   else if (*x + *width > clip_x + clip_width)
540   {
541     *width = clip_x + clip_width - *x;
542   }
543
544   if (*y < clip_y)
545   {
546     *height -= clip_y - *y;
547     *y = clip_y;
548   }
549   else if (*y + *height > clip_y + clip_height)
550   {
551     *height = clip_y + clip_height - *y;
552   }
553
554   return TRUE;
555 }
556
557 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
558                 int src_x, int src_y, int width, int height,
559                 int dst_x, int dst_y)
560 {
561   int dst_x_unclipped = dst_x;
562   int dst_y_unclipped = dst_y;
563
564   if (src_bitmap == NULL || dst_bitmap == NULL)
565     return;
566
567   if (DrawingDeactivated(dst_x, dst_y, width, height))
568     return;
569
570   if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
571       !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
572     return;
573
574   /* source x/y might need adjustment if destination x/y was clipped top/left */
575   src_x += dst_x - dst_x_unclipped;
576   src_y += dst_y - dst_y_unclipped;
577
578 #if defined(TARGET_SDL2)
579   /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
580   /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
581   /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
582      but is already fixed in SVN and should therefore finally be fixed with
583      the next official SDL release, which is probably version 1.2.14.) */
584   /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
585
586   if (src_bitmap == dst_bitmap)
587   {
588     /* needed when blitting directly to same bitmap -- should not be needed with
589        recent SDL libraries, but apparently does not work in 1.2.11 directly */
590
591     static Bitmap *tmp_bitmap = NULL;
592     static int tmp_bitmap_xsize = 0;
593     static int tmp_bitmap_ysize = 0;
594
595     /* start with largest static bitmaps for initial bitmap size ... */
596     if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
597     {
598       tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
599       tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
600     }
601
602     /* ... and allow for later re-adjustments due to custom artwork bitmaps */
603     if (src_bitmap->width > tmp_bitmap_xsize ||
604         src_bitmap->height > tmp_bitmap_ysize)
605     {
606       tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
607       tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
608
609       FreeBitmap(tmp_bitmap);
610
611       tmp_bitmap = NULL;
612     }
613
614     if (tmp_bitmap == NULL)
615       tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
616                                 DEFAULT_DEPTH);
617
618     sysCopyArea(src_bitmap, tmp_bitmap,
619                 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
620     sysCopyArea(tmp_bitmap, dst_bitmap,
621                 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
622
623     return;
624   }
625 #endif
626
627   sysCopyArea(src_bitmap, dst_bitmap,
628               src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
629 }
630
631 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
632                      int src_x, int src_y, int src_width, int src_height,
633                      int dst_x, int dst_y, int dst_width, int dst_height)
634 {
635   int src_xsize = (src_width  == 0 ? src_bitmap->width  : src_width);
636   int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
637   int dst_xsize = dst_width;
638   int dst_ysize = dst_height;
639   int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
640   int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
641   int x, y;
642
643   for (y = 0; y < src_ysteps; y++)
644   {
645     for (x = 0; x < src_xsteps; x++)
646     {
647       int draw_x = dst_x + x * src_xsize;
648       int draw_y = dst_y + y * src_ysize;
649       int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
650       int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
651
652       BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
653                  draw_x, draw_y);
654     }
655   }
656 }
657
658 void FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
659                    int fade_mode, int fade_delay, int post_delay,
660                    void (*draw_border_function)(void))
661 {
662   /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
663   if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
664     return;
665
666   SDLFadeRectangle(bitmap_cross, x, y, width, height,
667                    fade_mode, fade_delay, post_delay, draw_border_function);
668 }
669
670 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
671                    Pixel color)
672 {
673   if (DrawingDeactivated(x, y, width, height))
674     return;
675
676   if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
677     return;
678
679   sysFillRectangle(bitmap, x, y, width, height, color);
680 }
681
682 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
683 {
684   FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
685 }
686
687 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
688                                 int width, int height)
689 {
690   if (DrawingOnBackground(x, y))
691     BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
692   else
693     ClearRectangle(bitmap, x, y, width, height);
694 }
695
696 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
697                       int src_x, int src_y, int width, int height,
698                       int dst_x, int dst_y)
699 {
700   if (DrawingDeactivated(dst_x, dst_y, width, height))
701     return;
702
703   sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
704               dst_x, dst_y, BLIT_MASKED);
705 }
706
707 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
708                             int src_x, int src_y, int width, int height,
709                             int dst_x, int dst_y)
710 {
711   if (DrawingOnBackground(dst_x, dst_y))
712   {
713     /* draw background */
714     BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
715                dst_x, dst_y);
716
717     /* draw foreground */
718     BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
719                      dst_x, dst_y);
720   }
721   else
722     BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
723                dst_x, dst_y);
724 }
725
726 void BlitTexture(Bitmap *bitmap,
727                 int src_x, int src_y, int width, int height,
728                 int dst_x, int dst_y)
729 {
730   if (bitmap == NULL)
731     return;
732
733   SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
734                  BLIT_OPAQUE);
735 }
736
737 void BlitTextureMasked(Bitmap *bitmap,
738                        int src_x, int src_y, int width, int height,
739                        int dst_x, int dst_y)
740 {
741   if (bitmap == NULL)
742     return;
743
744   SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
745                  BLIT_MASKED);
746 }
747
748 void BlitToScreen(Bitmap *bitmap,
749                   int src_x, int src_y, int width, int height,
750                   int dst_x, int dst_y)
751 {
752   if (bitmap == NULL)
753     return;
754
755 #if USE_FINAL_SCREEN_BITMAP
756   BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
757              width, height, dst_x, dst_y);
758 #else
759   BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
760 #endif
761 }
762
763 void BlitToScreenMasked(Bitmap *bitmap,
764                         int src_x, int src_y, int width, int height,
765                         int dst_x, int dst_y)
766 {
767   if (bitmap == NULL)
768     return;
769
770 #if USE_FINAL_SCREEN_BITMAP
771   BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
772                    width, height, dst_x, dst_y);
773 #else
774   BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
775 #endif
776 }
777
778 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
779                          int to_x, int to_y)
780 {
781   SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
782 }
783
784 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
785                          int to_x, int to_y)
786 {
787   SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
788 }
789
790 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
791               int to_x, int to_y, Pixel pixel, int line_width)
792 {
793   int x, y;
794
795   for (x = 0; x < line_width; x++)
796   {
797     for (y = 0; y < line_width; y++)
798     {
799       int dx = x - line_width / 2;
800       int dy = y - line_width / 2;
801
802       if ((x == 0 && y == 0) ||
803           (x == 0 && y == line_width - 1) ||
804           (x == line_width - 1 && y == 0) ||
805           (x == line_width - 1 && y == line_width - 1))
806         continue;
807
808       SDLDrawLine(bitmap,
809                   from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
810     }
811   }
812 }
813
814 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
815 {
816   int line_width = 4;
817   int i;
818
819   for (i = 0; i < num_points - 1; i++)
820     DrawLine(bitmap, points[i].x, points[i].y,
821              points[i + 1].x, points[i + 1].y, pixel, line_width);
822
823   /*
824   SDLDrawLines(bitmap->surface, points, num_points, pixel);
825   */
826 }
827
828 Pixel GetPixel(Bitmap *bitmap, int x, int y)
829 {
830   if (x < 0 || x >= bitmap->width ||
831       y < 0 || y >= bitmap->height)
832     return BLACK_PIXEL;
833
834   return SDLGetPixel(bitmap, x, y);
835 }
836
837 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
838                       unsigned int color_g, unsigned int color_b)
839 {
840   return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
841 }
842
843 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
844 {
845   unsigned int color_r = (color >> 16) & 0xff;
846   unsigned int color_g = (color >>  8) & 0xff;
847   unsigned int color_b = (color >>  0) & 0xff;
848
849   return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
850 }
851
852 void KeyboardAutoRepeatOn(void)
853 {
854 #if defined(TARGET_SDL2)
855   keyrepeat_status = TRUE;
856 #else
857   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
858                       SDL_DEFAULT_REPEAT_INTERVAL / 2);
859   SDL_EnableUNICODE(1);
860 #endif
861 }
862
863 void KeyboardAutoRepeatOff(void)
864 {
865 #if defined(TARGET_SDL2)
866   keyrepeat_status = FALSE;
867 #else
868   SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
869   SDL_EnableUNICODE(0);
870 #endif
871 }
872
873 boolean SetVideoMode(boolean fullscreen)
874 {
875   return SDLSetVideoMode(&backbuffer, fullscreen);
876 }
877
878 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
879 {
880   if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
881       (!fullscreen && video.fullscreen_enabled))
882     fullscreen = SetVideoMode(fullscreen);
883
884   return fullscreen;
885 }
886
887 Bitmap *LoadImage(char *filename)
888 {
889   Bitmap *new_bitmap;
890
891   new_bitmap = SDLLoadImage(filename);
892
893   if (new_bitmap)
894     new_bitmap->source_filename = getStringCopy(filename);
895
896   return new_bitmap;
897 }
898
899 Bitmap *LoadCustomImage(char *basename)
900 {
901   char *filename = getCustomImageFilename(basename);
902   Bitmap *new_bitmap;
903
904   if (filename == NULL)
905     Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
906
907   if ((new_bitmap = LoadImage(filename)) == NULL)
908     Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
909
910   return new_bitmap;
911 }
912
913 void ReloadCustomImage(Bitmap *bitmap, char *basename)
914 {
915   char *filename = getCustomImageFilename(basename);
916   Bitmap *new_bitmap;
917
918   if (filename == NULL)         /* (should never happen) */
919   {
920     Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
921     return;
922   }
923
924   if (strEqual(filename, bitmap->source_filename))
925   {
926     /* The old and new image are the same (have the same filename and path).
927        This usually means that this image does not exist in this graphic set
928        and a fallback to the existing image is done. */
929
930     return;
931   }
932
933   if ((new_bitmap = LoadImage(filename)) == NULL)
934   {
935     Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
936     return;
937   }
938
939   if (bitmap->width != new_bitmap->width ||
940       bitmap->height != new_bitmap->height)
941   {
942     Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
943           filename);
944     FreeBitmap(new_bitmap);
945     return;
946   }
947
948   TransferBitmapPointers(new_bitmap, bitmap);
949   free(new_bitmap);
950 }
951
952 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
953 {
954   Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
955
956   return dst_bitmap;
957 }
958
959 static void SetMaskedBitmapSurface(Bitmap *bitmap)
960 {
961   if (bitmap == NULL)
962     return;
963
964   SDL_Surface *surface = bitmap->surface;
965
966   if (bitmap->surface_masked)
967     SDL_FreeSurface(bitmap->surface_masked);
968
969   SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
970                   SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
971
972   if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
973     Error(ERR_EXIT, "SDL_DisplayFormat() failed");
974
975   SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
976 }
977
978 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
979 {
980   if (bitmaps[IMG_BITMAP_CUSTOM])
981   {
982     FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
983
984     bitmaps[IMG_BITMAP_CUSTOM] = NULL;
985   }
986
987   if (gfx.game_tile_size == gfx.standard_tile_size)
988   {
989     bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
990
991     return;
992   }
993
994   Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
995   int width  = bitmap->width  * gfx.game_tile_size / gfx.standard_tile_size;;
996   int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
997
998   Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
999
1000   bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1001   bitmaps[IMG_BITMAP_GAME]   = bitmap_new;
1002
1003   SetMaskedBitmapSurface(bitmap_new);
1004 }
1005
1006 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1007                                 int tile_size, boolean create_small_bitmaps)
1008 {
1009   Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1010   Bitmap *tmp_bitmap_final = NULL;
1011   Bitmap *tmp_bitmap_0 = NULL;
1012   Bitmap *tmp_bitmap_1 = NULL;
1013   Bitmap *tmp_bitmap_2 = NULL;
1014   Bitmap *tmp_bitmap_4 = NULL;
1015   Bitmap *tmp_bitmap_8 = NULL;
1016   Bitmap *tmp_bitmap_16 = NULL;
1017   Bitmap *tmp_bitmap_32 = NULL;
1018   int width_final, height_final;
1019   int width_0, height_0;
1020   int width_1, height_1;
1021   int width_2, height_2;
1022   int width_4, height_4;
1023   int width_8, height_8;
1024   int width_16, height_16;
1025   int width_32, height_32;
1026   int old_width, old_height;
1027   int i;
1028
1029   print_timestamp_init("CreateScaledBitmaps");
1030
1031   old_width  = old_bitmap->width;
1032   old_height = old_bitmap->height;
1033
1034   /* calculate new image dimensions for final image size */
1035   width_final  = old_width  * zoom_factor;
1036   height_final = old_height * zoom_factor;
1037
1038   /* get image with final size (this might require scaling up) */
1039   /* ("final" size may result in non-standard tile size image) */
1040   if (zoom_factor != 1)
1041     tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1042   else
1043     tmp_bitmap_final = old_bitmap;
1044
1045   UPDATE_BUSY_STATE();
1046
1047   width_0  = width_1  = width_final;
1048   height_0 = height_1 = height_final;
1049
1050   tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1051
1052   if (create_small_bitmaps)
1053   {
1054     /* check if we have a non-gameplay tile size image */
1055     if (tile_size != gfx.game_tile_size)
1056     {
1057       /* get image with gameplay tile size */
1058       width_0  = width_final  * gfx.game_tile_size / tile_size;
1059       height_0 = height_final * gfx.game_tile_size / tile_size;
1060
1061       if (width_0 == old_width)
1062         tmp_bitmap_0 = old_bitmap;
1063       else if (width_0 == width_final)
1064         tmp_bitmap_0 = tmp_bitmap_final;
1065       else
1066         tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1067
1068       UPDATE_BUSY_STATE();
1069     }
1070
1071     /* check if we have a non-standard tile size image */
1072     if (tile_size != gfx.standard_tile_size)
1073     {
1074       /* get image with standard tile size */
1075       width_1  = width_final  * gfx.standard_tile_size / tile_size;
1076       height_1 = height_final * gfx.standard_tile_size / tile_size;
1077
1078       if (width_1 == old_width)
1079         tmp_bitmap_1 = old_bitmap;
1080       else if (width_1 == width_final)
1081         tmp_bitmap_1 = tmp_bitmap_final;
1082       else if (width_1 == width_0)
1083         tmp_bitmap_1 = tmp_bitmap_0;
1084       else
1085         tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1086
1087       UPDATE_BUSY_STATE();
1088     }
1089
1090     /* calculate new image dimensions for small images */
1091     width_2  = width_1  / 2;
1092     height_2 = height_1 / 2;
1093     width_4  = width_1  / 4;
1094     height_4 = height_1 / 4;
1095     width_8  = width_1  / 8;
1096     height_8 = height_1 / 8;
1097     width_16  = width_1  / 16;
1098     height_16 = height_1 / 16;
1099     width_32  = width_1  / 32;
1100     height_32 = height_1 / 32;
1101
1102     /* get image with 1/2 of normal size (for use in the level editor) */
1103     if (width_2 == old_width)
1104       tmp_bitmap_2 = old_bitmap;
1105     else
1106       tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1107
1108     UPDATE_BUSY_STATE();
1109
1110     /* get image with 1/4 of normal size (for use in the level editor) */
1111     if (width_4 == old_width)
1112       tmp_bitmap_4 = old_bitmap;
1113     else
1114       tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1115
1116     UPDATE_BUSY_STATE();
1117
1118     /* get image with 1/8 of normal size (for use on the preview screen) */
1119     if (width_8 == old_width)
1120       tmp_bitmap_8 = old_bitmap;
1121     else
1122       tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1123
1124     UPDATE_BUSY_STATE();
1125
1126     /* get image with 1/16 of normal size (for use on the preview screen) */
1127     if (width_16 == old_width)
1128       tmp_bitmap_16 = old_bitmap;
1129     else
1130       tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1131
1132     UPDATE_BUSY_STATE();
1133
1134     /* get image with 1/32 of normal size (for use on the preview screen) */
1135     if (width_32 == old_width)
1136       tmp_bitmap_32 = old_bitmap;
1137     else
1138       tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1139
1140     UPDATE_BUSY_STATE();
1141
1142     bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1143     bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1144     bitmaps[IMG_BITMAP_8x8]   = tmp_bitmap_4;
1145     bitmaps[IMG_BITMAP_4x4]   = tmp_bitmap_8;
1146     bitmaps[IMG_BITMAP_2x2]   = tmp_bitmap_16;
1147     bitmaps[IMG_BITMAP_1x1]   = tmp_bitmap_32;
1148
1149     if (width_0 != width_1)
1150       bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1151
1152     if (bitmaps[IMG_BITMAP_CUSTOM])
1153       bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1154     else
1155       bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1156
1157     boolean free_old_bitmap = TRUE;
1158
1159     for (i = 0; i < NUM_IMG_BITMAPS; i++)
1160       if (bitmaps[i] == old_bitmap)
1161         free_old_bitmap = FALSE;
1162
1163     if (free_old_bitmap)
1164       FreeBitmap(old_bitmap);
1165   }
1166   else
1167   {
1168     bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1169   }
1170
1171   // create corresponding bitmaps for masked blitting
1172   for (i = 0; i < NUM_IMG_BITMAPS; i++)
1173     if (bitmaps[i] != NULL &&
1174         bitmaps[i] != old_bitmap)
1175       SetMaskedBitmapSurface(bitmaps[i]);
1176
1177   UPDATE_BUSY_STATE();
1178
1179   print_timestamp_done("CreateScaledBitmaps");
1180 }
1181
1182 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1183                                   int tile_size)
1184 {
1185   CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1186 }
1187
1188 void CreateBitmapTextures(Bitmap **bitmaps)
1189 {
1190   SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1191 }
1192
1193 void FreeBitmapTextures(Bitmap **bitmaps)
1194 {
1195   SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1196 }
1197
1198 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1199 {
1200   CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1201 }
1202
1203
1204 /* ------------------------------------------------------------------------- */
1205 /* mouse pointer functions                                                   */
1206 /* ------------------------------------------------------------------------- */
1207
1208 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER            0
1209
1210 /* XPM image definitions */
1211 static const char *cursor_image_none[] =
1212 {
1213   /* width height num_colors chars_per_pixel */
1214   "    16    16        3            1",
1215
1216   /* colors */
1217   "X c #000000",
1218   ". c #ffffff",
1219   "  c None",
1220
1221   /* pixels */
1222   "                ",
1223   "                ",
1224   "                ",
1225   "                ",
1226   "                ",
1227   "                ",
1228   "                ",
1229   "                ",
1230   "                ",
1231   "                ",
1232   "                ",
1233   "                ",
1234   "                ",
1235   "                ",
1236   "                ",
1237   "                ",
1238
1239   /* hot spot */
1240   "0,0"
1241 };
1242
1243 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1244 static const char *cursor_image_dot[] =
1245 {
1246   /* width height num_colors chars_per_pixel */
1247   "    16    16        3            1",
1248
1249   /* colors */
1250   "X c #000000",
1251   ". c #ffffff",
1252   "  c None",
1253
1254   /* pixels */
1255   " X              ",
1256   "X.X             ",
1257   " X              ",
1258   "                ",
1259   "                ",
1260   "                ",
1261   "                ",
1262   "                ",
1263   "                ",
1264   "                ",
1265   "                ",
1266   "                ",
1267   "                ",
1268   "                ",
1269   "                ",
1270   "                ",
1271
1272   /* hot spot */
1273   "1,1"
1274 };
1275 static const char **cursor_image_playfield = cursor_image_dot;
1276 #else
1277 /* some people complained about a "white dot" on the screen and thought it
1278    was a graphical error... OK, let's just remove the whole pointer :-) */
1279 static const char **cursor_image_playfield = cursor_image_none;
1280 #endif
1281
1282 static const int cursor_bit_order = BIT_ORDER_MSB;
1283
1284 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1285 {
1286   struct MouseCursorInfo *cursor;
1287   boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1288   int header_lines = 4;
1289   int x, y, i;
1290
1291   cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1292
1293   sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1294
1295   i = -1;
1296   for (y = 0; y < cursor->width; y++)
1297   {
1298     for (x = 0; x < cursor->height; x++)
1299     {
1300       int bit_nr = x % 8;
1301       int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1302
1303       if (bit_nr == 0)
1304       {
1305         i++;
1306         cursor->data[i] = cursor->mask[i] = 0;
1307       }
1308
1309       switch (image[header_lines + y][x])
1310       {
1311         case 'X':
1312           cursor->data[i] |= bit_mask;
1313           cursor->mask[i] |= bit_mask;
1314           break;
1315
1316         case '.':
1317           cursor->mask[i] |= bit_mask;
1318           break;
1319
1320         case ' ':
1321           break;
1322       }
1323     }
1324   }
1325
1326   sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1327
1328   return cursor;
1329 }
1330
1331 void SetMouseCursor(int mode)
1332 {
1333   static struct MouseCursorInfo *cursor_none = NULL;
1334   static struct MouseCursorInfo *cursor_playfield = NULL;
1335   struct MouseCursorInfo *cursor_new;
1336
1337   if (cursor_none == NULL)
1338     cursor_none = get_cursor_from_image(cursor_image_none);
1339
1340   if (cursor_playfield == NULL)
1341     cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1342
1343   cursor_new = (mode == CURSOR_DEFAULT   ? NULL :
1344                 mode == CURSOR_NONE      ? cursor_none :
1345                 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1346
1347   SDLSetMouseCursor(cursor_new);
1348
1349   gfx.cursor_mode = mode;
1350 }
1351
1352
1353 /* ========================================================================= */
1354 /* audio functions                                                           */
1355 /* ========================================================================= */
1356
1357 void OpenAudio(void)
1358 {
1359   /* always start with reliable default values */
1360   audio.sound_available = FALSE;
1361   audio.music_available = FALSE;
1362   audio.loops_available = FALSE;
1363
1364   audio.sound_enabled = FALSE;
1365   audio.sound_deactivated = FALSE;
1366
1367   audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1368   audio.mixer_pid = 0;
1369   audio.device_name = NULL;
1370   audio.device_fd = -1;
1371
1372   audio.num_channels = 0;
1373   audio.music_channel = 0;
1374   audio.first_sound_channel = 0;
1375
1376   SDLOpenAudio();
1377 }
1378
1379 void CloseAudio(void)
1380 {
1381   SDLCloseAudio();
1382
1383   audio.sound_enabled = FALSE;
1384 }
1385
1386 void SetAudioMode(boolean enabled)
1387 {
1388   if (!audio.sound_available)
1389     return;
1390
1391   audio.sound_enabled = enabled;
1392 }
1393
1394
1395 /* ========================================================================= */
1396 /* event functions                                                           */
1397 /* ========================================================================= */
1398
1399 void InitEventFilter(EventFilter filter_function)
1400 {
1401   /* set event filter to filter out certain events */
1402 #if defined(TARGET_SDL2)
1403   SDL_SetEventFilter(filter_function, NULL);
1404 #else
1405   SDL_SetEventFilter(filter_function);
1406 #endif
1407 }
1408
1409 boolean PendingEvent(void)
1410 {
1411   return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1412 }
1413
1414 void NextEvent(Event *event)
1415 {
1416   SDLNextEvent(event);
1417 }
1418
1419 void PeekEvent(Event *event)
1420 {
1421 #if defined(TARGET_SDL2)
1422   SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1423 #else
1424   SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1425 #endif
1426 }
1427
1428 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1429 {
1430 #if defined(TARGET_SDL2)
1431   /* key up/down events in SDL2 do not return text characters anymore */
1432   return event->keysym.sym;
1433 #else
1434
1435 #if ENABLE_UNUSED_CODE
1436   printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1437          (int)event->keysym.unicode,
1438          (int)event->keysym.sym,
1439          (int)SDL_GetModState());
1440 #endif
1441
1442   if (with_modifiers &&
1443       event->keysym.unicode > 0x0000 &&
1444       event->keysym.unicode < 0x2000)
1445     return event->keysym.unicode;
1446   else
1447     return event->keysym.sym;
1448
1449 #endif
1450 }
1451
1452 KeyMod HandleKeyModState(Key key, int key_status)
1453 {
1454   static KeyMod current_modifiers = KMOD_None;
1455
1456   if (key != KSYM_UNDEFINED)    /* new key => check for modifier key change */
1457   {
1458     KeyMod new_modifier = KMOD_None;
1459
1460     switch(key)
1461     {
1462       case KSYM_Shift_L:
1463         new_modifier = KMOD_Shift_L;
1464         break;
1465       case KSYM_Shift_R:
1466         new_modifier = KMOD_Shift_R;
1467         break;
1468       case KSYM_Control_L:
1469         new_modifier = KMOD_Control_L;
1470         break;
1471       case KSYM_Control_R:
1472         new_modifier = KMOD_Control_R;
1473         break;
1474       case KSYM_Meta_L:
1475         new_modifier = KMOD_Meta_L;
1476         break;
1477       case KSYM_Meta_R:
1478         new_modifier = KMOD_Meta_R;
1479         break;
1480       case KSYM_Alt_L:
1481         new_modifier = KMOD_Alt_L;
1482         break;
1483       case KSYM_Alt_R:
1484         new_modifier = KMOD_Alt_R;
1485         break;
1486       default:
1487         break;
1488     }
1489
1490     if (key_status == KEY_PRESSED)
1491       current_modifiers |= new_modifier;
1492     else
1493       current_modifiers &= ~new_modifier;
1494   }
1495
1496   return current_modifiers;
1497 }
1498
1499 KeyMod GetKeyModState()
1500 {
1501   return (KeyMod)SDL_GetModState();
1502 }
1503
1504 KeyMod GetKeyModStateFromEvents()
1505 {
1506   /* always use key modifier state as tracked from key events (this is needed
1507      if the modifier key event was injected into the event queue, but the key
1508      was not really pressed on keyboard -- SDL_GetModState() seems to directly
1509      query the keys as held pressed on the keyboard) -- this case is currently
1510      only used to filter out clipboard insert events from "True X-Mouse" tool */
1511
1512   return HandleKeyModState(KSYM_UNDEFINED, 0);
1513 }
1514
1515 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1516 {
1517   if (event->type != EVENT_CLIENTMESSAGE)
1518     return FALSE;
1519
1520   return TRUE;          /* the only possible message here is SDL_QUIT */
1521 }
1522
1523
1524 /* ========================================================================= */
1525 /* joystick functions                                                        */
1526 /* ========================================================================= */
1527
1528 void InitJoysticks()
1529 {
1530   int i;
1531
1532 #if defined(NO_JOYSTICK)
1533   return;       /* joysticks generally deactivated by compile-time directive */
1534 #endif
1535
1536   /* always start with reliable default values */
1537   joystick.status = JOYSTICK_NOT_AVAILABLE;
1538   for (i = 0; i < MAX_PLAYERS; i++)
1539     joystick.fd[i] = -1;                /* joystick device closed */
1540
1541   SDLInitJoysticks();
1542 }
1543
1544 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1545 {
1546   return SDLReadJoystick(nr, x, y, b1, b2);
1547 }