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