1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
24 #define ENABLE_UNUSED_CODE 0 // currently unused functions
27 // ============================================================================
29 // ============================================================================
31 struct ProgramInfo program;
32 struct NetworkInfo network;
33 struct RuntimeInfo runtime;
34 struct OptionInfo options;
35 struct VideoSystemInfo video;
36 struct AudioSystemInfo audio;
38 struct TileCursorInfo tile_cursor;
39 struct OverlayInfo overlay;
40 struct ArtworkInfo artwork;
41 struct JoystickInfo joystick;
42 struct SetupInfo setup;
45 LevelDirTree *leveldir_first_all = NULL;
46 LevelDirTree *leveldir_first = NULL;
47 LevelDirTree *leveldir_current = NULL;
50 struct LevelSetInfo levelset;
51 struct LevelStats level_stats[MAX_LEVELS];
53 DrawWindow *window = NULL;
54 DrawBuffer *backbuffer = NULL;
55 DrawBuffer *drawto = NULL;
57 int button_status = MB_NOT_PRESSED;
58 boolean motion_status = FALSE;
59 int wheel_steps = DEFAULT_WHEEL_STEPS;
60 boolean keyrepeat_status = TRUE;
61 boolean textinput_status = FALSE;
63 int redraw_mask = REDRAW_NONE;
68 // ============================================================================
69 // init/close functions
70 // ============================================================================
72 void InitProgramInfo(char *argv0, char *config_filename, char *userdata_subdir,
73 char *program_title, char *icon_title,
74 char *icon_filename, char *cookie_prefix,
75 char *program_version_string, int program_version)
77 program.command_basepath = getBasePath(argv0);
78 program.command_basename = getBaseName(argv0);
80 program.config_filename = config_filename;
82 program.userdata_subdir = userdata_subdir;
83 program.userdata_path = getMainUserGameDataDir();
85 program.program_title = program_title;
86 program.window_title = "(undefined)";
87 program.icon_title = icon_title;
89 program.icon_filename = icon_filename;
91 program.cookie_prefix = cookie_prefix;
93 program.version_super = VERSION_SUPER(program_version);
94 program.version_major = VERSION_MAJOR(program_version);
95 program.version_minor = VERSION_MINOR(program_version);
96 program.version_patch = VERSION_PATCH(program_version);
97 program.version_ident = program_version;
99 program.version_string = program_version_string;
101 program.log_filename[LOG_OUT_ID] = getLogFilename(LOG_OUT_BASENAME);
102 program.log_filename[LOG_ERR_ID] = getLogFilename(LOG_ERR_BASENAME);
103 program.log_file[LOG_OUT_ID] = program.log_file_default[LOG_OUT_ID] = stdout;
104 program.log_file[LOG_ERR_ID] = program.log_file_default[LOG_ERR_ID] = stderr;
106 program.headless = FALSE;
109 void InitNetworkInfo(boolean enabled, boolean connected, boolean serveronly,
110 char *server_host, int server_port)
112 network.enabled = enabled;
113 network.connected = connected;
114 network.serveronly = serveronly;
116 network.server_host = server_host;
117 network.server_port = server_port;
119 network.server_thread = NULL;
120 network.is_server_thread = FALSE;
123 void InitRuntimeInfo()
125 #if defined(HAS_TOUCH_DEVICE)
126 runtime.uses_touch_device = TRUE;
128 runtime.uses_touch_device = FALSE;
131 runtime.api_server = setup.api_server;
134 void InitScoresInfo(void)
136 char *global_scores_dir = getPath2(getCommonDataDir(), SCORES_DIRECTORY);
138 program.global_scores = directoryExists(global_scores_dir);
139 program.many_scores_per_name = !program.global_scores;
144 if (program.global_scores)
146 Debug("internal:path", "Using global, multi-user scores directory '%s'.",
148 Debug("internal:path", "Remove to enable single-user scores directory.");
149 Debug("internal:path", "(This enables multipe score entries per user.)");
153 Debug("internal:path", "Using private, single-user scores directory.");
158 free(global_scores_dir);
161 void SetWindowTitle(void)
163 program.window_title = program.window_title_function();
168 void InitWindowTitleFunction(char *(*window_title_function)(void))
170 program.window_title_function = window_title_function;
173 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
175 program.exit_message_function = exit_message_function;
178 void InitExitFunction(void (*exit_function)(int))
180 program.exit_function = exit_function;
182 // set signal handlers to custom exit function
183 // signal(SIGINT, exit_function);
184 signal(SIGTERM, exit_function);
186 // set exit function to automatically cleanup SDL stuff after exit()
190 void InitPlatformDependentStuff(void)
192 InitEmscriptenFilesystem();
194 // this is initialized in GetOptions(), but may already be used before
195 options.verbose = TRUE;
199 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
201 if (SDL_Init(sdl_init_flags) < 0)
202 Fail("SDL_Init() failed: %s", SDL_GetError());
207 void ClosePlatformDependentStuff(void)
212 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
213 int real_sx, int real_sy,
214 int full_sxsize, int full_sysize,
215 Bitmap *field_save_buffer)
221 gfx.real_sx = real_sx;
222 gfx.real_sy = real_sy;
223 gfx.full_sxsize = full_sxsize;
224 gfx.full_sysize = full_sysize;
226 gfx.field_save_buffer = field_save_buffer;
228 SetDrawDeactivationMask(REDRAW_NONE); // do not deactivate drawing
229 SetDrawBackgroundMask(REDRAW_NONE); // deactivate masked drawing
232 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
234 gfx.game_tile_size = game_tile_size;
235 gfx.standard_tile_size = standard_tile_size;
238 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
246 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
254 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
262 void InitGfxWindowInfo(int win_xsize, int win_ysize)
264 if (win_xsize != gfx.win_xsize || win_ysize != gfx.win_ysize)
266 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize);
268 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize);
270 ReCreateBitmap(&gfx.fade_bitmap_backup, win_xsize, win_ysize);
271 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize);
272 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize);
273 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize);
275 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
278 gfx.win_xsize = win_xsize;
279 gfx.win_ysize = win_ysize;
281 gfx.background_bitmap_mask = REDRAW_NONE;
284 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
286 // currently only used by MSDOS code to alloc VRAM buffer, if available
287 // 2009-03-24: also (temporarily?) used for overlapping blit workaround
288 gfx.scrollbuffer_width = scrollbuffer_width;
289 gfx.scrollbuffer_height = scrollbuffer_height;
292 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
294 gfx.clipping_enabled = enabled;
297 gfx.clip_width = width;
298 gfx.clip_height = height;
301 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
303 gfx.draw_busy_anim_function = draw_busy_anim_function;
306 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int))
308 gfx.draw_global_anim_function = draw_global_anim_function;
311 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
313 gfx.draw_global_border_function = draw_global_border_function;
316 void InitGfxDrawTileCursorFunction(void (*draw_tile_cursor_function)(int))
318 gfx.draw_tile_cursor_function = draw_tile_cursor_function;
321 void InitGfxCustomArtworkInfo(void)
323 gfx.override_level_graphics = FALSE;
324 gfx.override_level_sounds = FALSE;
325 gfx.override_level_music = FALSE;
327 gfx.draw_init_text = TRUE;
330 void InitGfxOtherSettings(void)
332 gfx.cursor_mode = CURSOR_DEFAULT;
333 gfx.cursor_mode_override = CURSOR_UNDEFINED;
334 gfx.cursor_mode_final = gfx.cursor_mode;
336 // prevent initially displaying custom mouse cursor in upper left corner
337 gfx.mouse_x = POS_OFFSCREEN;
338 gfx.mouse_y = POS_OFFSCREEN;
341 void InitTileCursorInfo(void)
343 tile_cursor.enabled = FALSE;
344 tile_cursor.active = FALSE;
345 tile_cursor.moving = FALSE;
347 tile_cursor.xpos = 0;
348 tile_cursor.ypos = 0;
351 tile_cursor.target_x = 0;
352 tile_cursor.target_y = 0;
357 tile_cursor.xsn_debug = FALSE;
360 void InitOverlayInfo(void)
362 overlay.enabled = FALSE;
363 overlay.active = FALSE;
365 overlay.show_grid = FALSE;
367 overlay.grid_button_highlight = CHAR_GRID_BUTTON_NONE;
368 overlay.grid_button_action = JOY_NO_ACTION;
370 SetOverlayGridSizeAndButtons();
372 #if defined(USE_TOUCH_INPUT_OVERLAY)
373 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
374 overlay.enabled = TRUE;
378 void SetOverlayGridSizeAndButtons(void)
380 int nr = GRID_ACTIVE_NR();
383 overlay.grid_xsize = setup.touch.grid_xsize[nr];
384 overlay.grid_ysize = setup.touch.grid_ysize[nr];
386 for (x = 0; x < MAX_GRID_XSIZE; x++)
387 for (y = 0; y < MAX_GRID_YSIZE; y++)
388 overlay.grid_button[x][y] = setup.touch.grid_button[nr][x][y];
391 void SetTileCursorEnabled(boolean enabled)
393 tile_cursor.enabled = enabled;
396 void SetTileCursorActive(boolean active)
398 tile_cursor.active = active;
401 void SetTileCursorTargetXY(int x, int y)
403 // delayed placement of tile selection cursor at target position
404 // (tile cursor will be moved to target position step by step)
406 tile_cursor.xpos = x;
407 tile_cursor.ypos = y;
408 tile_cursor.target_x = tile_cursor.sx + x * gfx.game_tile_size;
409 tile_cursor.target_y = tile_cursor.sy + y * gfx.game_tile_size;
411 tile_cursor.moving = TRUE;
414 void SetTileCursorXY(int x, int y)
416 // immediate placement of tile selection cursor at target position
418 SetTileCursorTargetXY(x, y);
420 tile_cursor.x = tile_cursor.target_x;
421 tile_cursor.y = tile_cursor.target_y;
423 tile_cursor.moving = FALSE;
426 void SetTileCursorSXSY(int sx, int sy)
432 void SetOverlayEnabled(boolean enabled)
434 overlay.enabled = enabled;
437 void SetOverlayActive(boolean active)
439 overlay.active = active;
442 void SetOverlayShowGrid(boolean show_grid)
444 overlay.show_grid = show_grid;
446 SetOverlayActive(show_grid);
449 SetOverlayEnabled(TRUE);
452 boolean GetOverlayEnabled(void)
454 return overlay.enabled;
457 boolean GetOverlayActive(void)
459 return overlay.active;
462 void SetDrawDeactivationMask(int draw_deactivation_mask)
464 gfx.draw_deactivation_mask = draw_deactivation_mask;
467 int GetDrawDeactivationMask(void)
469 return gfx.draw_deactivation_mask;
472 void SetDrawBackgroundMask(int draw_background_mask)
474 gfx.draw_background_mask = draw_background_mask;
477 static void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
479 if (background_bitmap_tile != NULL)
480 gfx.background_bitmap_mask |= mask;
482 gfx.background_bitmap_mask &= ~mask;
484 if (background_bitmap_tile == NULL) // empty background requested
487 if (mask == REDRAW_ALL)
488 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
489 0, 0, video.width, video.height);
490 else if (mask == REDRAW_FIELD)
491 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
492 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
493 else if (mask == REDRAW_DOOR_1)
494 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
495 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
498 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
500 // remove every mask before setting mask for window
501 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
502 SetBackgroundBitmap(NULL, 0xffff); // !!! FIX THIS !!!
503 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
506 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
508 // remove window area mask before setting mask for main area
509 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
510 SetBackgroundBitmap(NULL, REDRAW_ALL); // !!! FIX THIS !!!
511 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
514 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
516 // remove window area mask before setting mask for door area
517 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
518 SetBackgroundBitmap(NULL, REDRAW_ALL); // !!! FIX THIS !!!
519 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
523 // ============================================================================
525 // ============================================================================
527 static int GetRealDepth(int depth)
529 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
532 static void sysFillRectangle(Bitmap *bitmap, int x, int y,
533 int width, int height, Pixel color)
535 SDLFillRectangle(bitmap, x, y, width, height, color);
537 if (bitmap == backbuffer)
538 SetRedrawMaskFromArea(x, y, width, height);
541 static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
542 int src_x, int src_y, int width, int height,
543 int dst_x, int dst_y, int mask_mode)
545 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
546 dst_x, dst_y, mask_mode);
548 if (dst_bitmap == backbuffer)
549 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
552 void LimitScreenUpdates(boolean enable)
554 SDLLimitScreenUpdates(enable);
557 void InitVideoDefaults(void)
559 video.default_depth = 32;
562 void InitVideoDisplay(void)
564 if (program.headless)
567 SDLInitVideoDisplay();
571 void CloseVideoDisplay(void)
573 KeyboardAutoRepeatOn();
575 SDL_QuitSubSystem(SDL_INIT_VIDEO);
578 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
581 video.height = height;
582 video.depth = GetRealDepth(depth);
584 video.screen_width = width;
585 video.screen_height = height;
586 video.screen_xoffset = 0;
587 video.screen_yoffset = 0;
589 video.fullscreen_available = FULLSCREEN_STATUS;
590 video.fullscreen_enabled = FALSE;
592 video.window_scaling_available = WINDOW_SCALING_STATUS;
594 video.frame_counter = 0;
595 video.frame_delay = 0;
596 video.frame_delay_value = GAME_FRAME_DELAY;
598 video.shifted_up = FALSE;
599 video.shifted_up_pos = 0;
600 video.shifted_up_pos_last = 0;
601 video.shifted_up_delay = 0;
602 video.shifted_up_delay_value = ONE_SECOND_DELAY / 4;
604 SDLInitVideoBuffer(fullscreen);
606 video.initialized = !program.headless;
611 static void FreeBitmapPointers(Bitmap *bitmap)
616 SDLFreeBitmapPointers(bitmap);
618 checked_free(bitmap->source_filename);
619 bitmap->source_filename = NULL;
622 static void TransferBitmapPointers(Bitmap *src_bitmap,
625 if (src_bitmap == NULL || dst_bitmap == NULL)
628 FreeBitmapPointers(dst_bitmap);
630 *dst_bitmap = *src_bitmap;
633 void FreeBitmap(Bitmap *bitmap)
638 FreeBitmapPointers(bitmap);
643 Bitmap *CreateBitmapStruct(void)
645 return checked_calloc(sizeof(Bitmap));
648 Bitmap *CreateBitmap(int width, int height, int depth)
650 Bitmap *new_bitmap = CreateBitmapStruct();
651 int real_width = MAX(1, width); // prevent zero bitmap width
652 int real_height = MAX(1, height); // prevent zero bitmap height
653 int real_depth = GetRealDepth(depth);
655 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
657 new_bitmap->width = real_width;
658 new_bitmap->height = real_height;
663 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
667 // if new bitmap size fits into old one, no need to re-create it
668 if (width <= (*bitmap)->width &&
669 height <= (*bitmap)->height)
672 // else adjust size so that old and new bitmap size fit into it
673 width = MAX(width, (*bitmap)->width);
674 height = MAX(height, (*bitmap)->height);
677 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
681 *bitmap = new_bitmap;
685 TransferBitmapPointers(new_bitmap, *bitmap);
691 static void CloseWindow(DrawWindow *window)
696 void SetRedrawMaskFromArea(int x, int y, int width, int height)
700 int x2 = x + width - 1;
701 int y2 = y + height - 1;
703 if (width == 0 || height == 0)
706 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
707 redraw_mask |= REDRAW_FIELD;
708 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
709 redraw_mask |= REDRAW_DOOR_1;
710 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
711 redraw_mask |= REDRAW_DOOR_2;
712 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
713 redraw_mask |= REDRAW_DOOR_3;
715 redraw_mask = REDRAW_ALL;
718 static boolean CheckDrawingArea(int x, int y, int width, int height,
721 if (draw_mask == REDRAW_NONE)
724 if (draw_mask & REDRAW_ALL)
727 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
730 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
733 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
736 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
742 boolean DrawingDeactivatedField(void)
744 if (program.headless)
747 if (gfx.draw_deactivation_mask & REDRAW_FIELD)
753 boolean DrawingDeactivated(int x, int y, int width, int height)
755 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
758 boolean DrawingOnBackground(int x, int y)
760 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
761 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
764 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
765 int *width, int *height, boolean is_dest)
767 int clip_x, clip_y, clip_width, clip_height;
769 if (gfx.clipping_enabled && is_dest) // only clip destination bitmap
771 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
772 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
773 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
774 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
780 clip_width = bitmap->width;
781 clip_height = bitmap->height;
784 // skip if rectangle completely outside bitmap
786 if (*x + *width <= clip_x ||
787 *y + *height <= clip_y ||
788 *x >= clip_x + clip_width ||
789 *y >= clip_y + clip_height)
792 // clip if rectangle overlaps bitmap
796 *width -= clip_x - *x;
799 else if (*x + *width > clip_x + clip_width)
801 *width = clip_x + clip_width - *x;
806 *height -= clip_y - *y;
809 else if (*y + *height > clip_y + clip_height)
811 *height = clip_y + clip_height - *y;
817 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
818 int src_x, int src_y, int width, int height,
819 int dst_x, int dst_y)
821 int dst_x_unclipped = dst_x;
822 int dst_y_unclipped = dst_y;
824 if (program.headless)
827 if (src_bitmap == NULL || dst_bitmap == NULL)
830 if (DrawingDeactivated(dst_x, dst_y, width, height))
833 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
834 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
837 // source x/y might need adjustment if destination x/y was clipped top/left
838 src_x += dst_x - dst_x_unclipped;
839 src_y += dst_y - dst_y_unclipped;
841 // !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!!
842 // !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!!
843 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
844 but is already fixed in SVN and should therefore finally be fixed with
845 the next official SDL release, which is probably version 1.2.14.) */
846 // !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!!
848 if (src_bitmap == dst_bitmap)
850 // needed when blitting directly to same bitmap -- should not be needed with
851 // recent SDL libraries, but apparently does not work in 1.2.11 directly
853 static Bitmap *tmp_bitmap = NULL;
854 static int tmp_bitmap_xsize = 0;
855 static int tmp_bitmap_ysize = 0;
857 // start with largest static bitmaps for initial bitmap size ...
858 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
860 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
861 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
864 // ... and allow for later re-adjustments due to custom artwork bitmaps
865 if (src_bitmap->width > tmp_bitmap_xsize ||
866 src_bitmap->height > tmp_bitmap_ysize)
868 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
869 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
871 FreeBitmap(tmp_bitmap);
876 if (tmp_bitmap == NULL)
877 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
880 sysCopyArea(src_bitmap, tmp_bitmap,
881 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
882 sysCopyArea(tmp_bitmap, dst_bitmap,
883 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
888 sysCopyArea(src_bitmap, dst_bitmap,
889 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
892 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
893 int src_x, int src_y, int src_width, int src_height,
894 int dst_x, int dst_y, int dst_width, int dst_height)
896 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
897 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
898 int dst_xsize = dst_width;
899 int dst_ysize = dst_height;
900 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
901 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
904 for (y = 0; y < src_ysteps; y++)
906 for (x = 0; x < src_xsteps; x++)
908 int draw_x = dst_x + x * src_xsize;
909 int draw_y = dst_y + y * src_ysize;
910 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
911 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
913 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
919 void FadeRectangle(int x, int y, int width, int height,
920 int fade_mode, int fade_delay, int post_delay,
921 void (*draw_border_function)(void))
923 // (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined)
924 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
927 SDLFadeRectangle(x, y, width, height,
928 fade_mode, fade_delay, post_delay, draw_border_function);
931 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
934 if (DrawingDeactivated(x, y, width, height))
937 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
940 sysFillRectangle(bitmap, x, y, width, height, color);
943 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
945 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
948 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
949 int width, int height)
951 if (DrawingOnBackground(x, y))
952 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
954 ClearRectangle(bitmap, x, y, width, height);
957 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
958 int src_x, int src_y, int width, int height,
959 int dst_x, int dst_y)
961 if (DrawingDeactivated(dst_x, dst_y, width, height))
964 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
965 dst_x, dst_y, BLIT_MASKED);
968 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
969 int src_x, int src_y, int width, int height,
970 int dst_x, int dst_y)
972 if (DrawingOnBackground(dst_x, dst_y))
975 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
979 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
983 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
987 void BlitTexture(Bitmap *bitmap,
988 int src_x, int src_y, int width, int height,
989 int dst_x, int dst_y)
994 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
998 void BlitTextureMasked(Bitmap *bitmap,
999 int src_x, int src_y, int width, int height,
1000 int dst_x, int dst_y)
1005 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
1009 void BlitToScreen(Bitmap *bitmap,
1010 int src_x, int src_y, int width, int height,
1011 int dst_x, int dst_y)
1016 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
1017 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
1018 width, height, dst_x, dst_y);
1020 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
1023 void BlitToScreenMasked(Bitmap *bitmap,
1024 int src_x, int src_y, int width, int height,
1025 int dst_x, int dst_y)
1030 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
1031 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
1032 width, height, dst_x, dst_y);
1034 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
1037 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
1040 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
1043 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
1046 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
1049 static void DrawLine(Bitmap *bitmap, int from_x, int from_y,
1050 int to_x, int to_y, Pixel pixel, int line_width)
1054 if (program.headless)
1057 for (x = 0; x < line_width; x++)
1059 for (y = 0; y < line_width; y++)
1061 int dx = x - line_width / 2;
1062 int dy = y - line_width / 2;
1064 if ((x == 0 && y == 0) ||
1065 (x == 0 && y == line_width - 1) ||
1066 (x == line_width - 1 && y == 0) ||
1067 (x == line_width - 1 && y == line_width - 1))
1071 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
1076 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
1081 for (i = 0; i < num_points - 1; i++)
1082 DrawLine(bitmap, points[i].x, points[i].y,
1083 points[i + 1].x, points[i + 1].y, pixel, line_width);
1086 SDLDrawLines(bitmap->surface, points, num_points, pixel);
1090 Pixel GetPixel(Bitmap *bitmap, int x, int y)
1092 if (program.headless)
1095 if (x < 0 || x >= bitmap->width ||
1096 y < 0 || y >= bitmap->height)
1099 return SDLGetPixel(bitmap, x, y);
1102 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
1103 unsigned int color_g, unsigned int color_b)
1105 if (program.headless)
1108 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
1111 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
1113 unsigned int color_r = (color >> 16) & 0xff;
1114 unsigned int color_g = (color >> 8) & 0xff;
1115 unsigned int color_b = (color >> 0) & 0xff;
1117 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
1120 void KeyboardAutoRepeatOn(void)
1122 keyrepeat_status = TRUE;
1125 void KeyboardAutoRepeatOff(void)
1127 keyrepeat_status = FALSE;
1130 boolean SetVideoMode(boolean fullscreen)
1132 return SDLSetVideoMode(fullscreen);
1135 void SetVideoFrameDelay(unsigned int frame_delay_value)
1137 video.frame_delay_value = frame_delay_value;
1140 unsigned int GetVideoFrameDelay(void)
1142 return video.frame_delay_value;
1145 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
1147 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
1148 (!fullscreen && video.fullscreen_enabled))
1149 fullscreen = SetVideoMode(fullscreen);
1154 Bitmap *LoadImage(char *filename)
1158 new_bitmap = SDLLoadImage(filename);
1161 new_bitmap->source_filename = getStringCopy(filename);
1166 Bitmap *LoadCustomImage(char *basename)
1168 char *filename = getCustomImageFilename(basename);
1171 if (filename == NULL)
1172 Fail("LoadCustomImage(): cannot find file '%s'", basename);
1174 if ((new_bitmap = LoadImage(filename)) == NULL)
1175 Fail("LoadImage('%s') failed", basename);
1180 void ReloadCustomImage(Bitmap *bitmap, char *basename)
1182 char *filename = getCustomImageFilename(basename);
1185 if (filename == NULL) // (should never happen)
1187 Warn("ReloadCustomImage(): cannot find file '%s'", basename);
1192 if (strEqual(filename, bitmap->source_filename))
1194 // The old and new image are the same (have the same filename and path).
1195 // This usually means that this image does not exist in this graphic set
1196 // and a fallback to the existing image is done.
1201 if ((new_bitmap = LoadImage(filename)) == NULL)
1203 Warn("LoadImage('%s') failed", basename);
1208 if (bitmap->width != new_bitmap->width ||
1209 bitmap->height != new_bitmap->height)
1211 Warn("ReloadCustomImage: new image '%s' has wrong dimensions",
1214 FreeBitmap(new_bitmap);
1219 TransferBitmapPointers(new_bitmap, bitmap);
1223 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1225 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
1228 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1230 if (bitmaps[IMG_BITMAP_CUSTOM])
1232 // check if original sized bitmap points to custom sized bitmap
1233 if (bitmaps[IMG_BITMAP_PTR_ORIGINAL] == bitmaps[IMG_BITMAP_CUSTOM])
1235 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_PTR_ORIGINAL]);
1237 // keep pointer of previous custom size bitmap
1238 bitmaps[IMG_BITMAP_OTHER] = bitmaps[IMG_BITMAP_CUSTOM];
1240 // set original bitmap pointer to scaled original bitmap of other size
1241 bitmaps[IMG_BITMAP_PTR_ORIGINAL] = bitmaps[IMG_BITMAP_OTHER];
1243 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_PTR_ORIGINAL]);
1247 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1250 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1253 if (gfx.game_tile_size == gfx.standard_tile_size)
1255 // set game bitmap pointer to standard sized bitmap (already existing)
1256 bitmaps[IMG_BITMAP_PTR_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1261 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1262 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1263 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1265 bitmaps[IMG_BITMAP_CUSTOM] = ZoomBitmap(bitmap, width, height);
1267 // set game bitmap pointer to custom sized bitmap (newly created)
1268 bitmaps[IMG_BITMAP_PTR_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1271 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1272 int tile_size, boolean create_small_bitmaps)
1274 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1275 Bitmap *tmp_bitmap_final = NULL;
1276 Bitmap *tmp_bitmap_0 = NULL;
1277 Bitmap *tmp_bitmap_1 = NULL;
1278 Bitmap *tmp_bitmap_2 = NULL;
1279 Bitmap *tmp_bitmap_4 = NULL;
1280 Bitmap *tmp_bitmap_8 = NULL;
1281 Bitmap *tmp_bitmap_16 = NULL;
1282 Bitmap *tmp_bitmap_32 = NULL;
1283 int width_final, height_final;
1284 int width_0, height_0;
1285 int width_1, height_1;
1286 int width_2, height_2;
1287 int width_4, height_4;
1288 int width_8, height_8;
1289 int width_16, height_16;
1290 int width_32, height_32;
1291 int old_width, old_height;
1294 print_timestamp_init("CreateScaledBitmaps");
1296 old_width = old_bitmap->width;
1297 old_height = old_bitmap->height;
1299 // calculate new image dimensions for final image size
1300 width_final = old_width * zoom_factor;
1301 height_final = old_height * zoom_factor;
1303 // get image with final size (this might require scaling up)
1304 // ("final" size may result in non-standard tile size image)
1305 if (zoom_factor != 1)
1306 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1308 tmp_bitmap_final = old_bitmap;
1310 UPDATE_BUSY_STATE();
1312 width_0 = width_1 = width_final;
1313 height_0 = height_1 = height_final;
1315 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1317 if (create_small_bitmaps)
1319 // check if we have a non-gameplay tile size image
1320 if (tile_size != gfx.game_tile_size)
1322 // get image with gameplay tile size
1323 width_0 = width_final * gfx.game_tile_size / tile_size;
1324 height_0 = height_final * gfx.game_tile_size / tile_size;
1326 if (width_0 == old_width)
1327 tmp_bitmap_0 = old_bitmap;
1328 else if (width_0 == width_final)
1329 tmp_bitmap_0 = tmp_bitmap_final;
1331 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1333 UPDATE_BUSY_STATE();
1336 // check if we have a non-standard tile size image
1337 if (tile_size != gfx.standard_tile_size)
1339 // get image with standard tile size
1340 width_1 = width_final * gfx.standard_tile_size / tile_size;
1341 height_1 = height_final * gfx.standard_tile_size / tile_size;
1343 if (width_1 == old_width)
1344 tmp_bitmap_1 = old_bitmap;
1345 else if (width_1 == width_final)
1346 tmp_bitmap_1 = tmp_bitmap_final;
1347 else if (width_1 == width_0)
1348 tmp_bitmap_1 = tmp_bitmap_0;
1350 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1352 UPDATE_BUSY_STATE();
1355 // calculate new image dimensions for small images
1356 width_2 = width_1 / 2;
1357 height_2 = height_1 / 2;
1358 width_4 = width_1 / 4;
1359 height_4 = height_1 / 4;
1360 width_8 = width_1 / 8;
1361 height_8 = height_1 / 8;
1362 width_16 = width_1 / 16;
1363 height_16 = height_1 / 16;
1364 width_32 = width_1 / 32;
1365 height_32 = height_1 / 32;
1367 // get image with 1/2 of normal size (for use in the level editor)
1368 if (width_2 == old_width)
1369 tmp_bitmap_2 = old_bitmap;
1371 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1373 UPDATE_BUSY_STATE();
1375 // get image with 1/4 of normal size (for use in the level editor)
1376 if (width_4 == old_width)
1377 tmp_bitmap_4 = old_bitmap;
1379 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1381 UPDATE_BUSY_STATE();
1383 // get image with 1/8 of normal size (for use on the preview screen)
1384 if (width_8 == old_width)
1385 tmp_bitmap_8 = old_bitmap;
1387 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1389 UPDATE_BUSY_STATE();
1391 // get image with 1/16 of normal size (for use on the preview screen)
1392 if (width_16 == old_width)
1393 tmp_bitmap_16 = old_bitmap;
1395 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1397 UPDATE_BUSY_STATE();
1399 // get image with 1/32 of normal size (for use on the preview screen)
1400 if (width_32 == old_width)
1401 tmp_bitmap_32 = old_bitmap;
1403 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1405 UPDATE_BUSY_STATE();
1407 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1408 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1409 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1410 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1411 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1412 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1414 if (width_0 != width_1)
1415 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1417 if (bitmaps[IMG_BITMAP_CUSTOM])
1418 bitmaps[IMG_BITMAP_PTR_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1420 bitmaps[IMG_BITMAP_PTR_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1422 // store the "final" (up-scaled) original bitmap, if not already stored
1424 int tmp_bitmap_final_nr = -1;
1426 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1427 if (bitmaps[i] == tmp_bitmap_final)
1428 tmp_bitmap_final_nr = i;
1430 if (tmp_bitmap_final_nr == -1) // scaled original bitmap not stored
1432 // store pointer of scaled original bitmap (not used for any other size)
1433 bitmaps[IMG_BITMAP_OTHER] = tmp_bitmap_final;
1435 // set original bitmap pointer to scaled original bitmap of other size
1436 bitmaps[IMG_BITMAP_PTR_ORIGINAL] = bitmaps[IMG_BITMAP_OTHER];
1440 // set original bitmap pointer to corresponding sized bitmap
1441 bitmaps[IMG_BITMAP_PTR_ORIGINAL] = bitmaps[tmp_bitmap_final_nr];
1444 // free the "old" (unscaled) original bitmap, if not already stored
1446 boolean free_old_bitmap = TRUE;
1448 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1449 if (bitmaps[i] == old_bitmap)
1450 free_old_bitmap = FALSE;
1452 if (free_old_bitmap)
1454 // copy image filename from old to new standard sized bitmap
1455 bitmaps[IMG_BITMAP_STANDARD]->source_filename =
1456 getStringCopy(old_bitmap->source_filename);
1458 FreeBitmap(old_bitmap);
1463 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1465 // set original bitmap pointer to corresponding sized bitmap
1466 bitmaps[IMG_BITMAP_PTR_ORIGINAL] = bitmaps[IMG_BITMAP_32x32];
1468 if (old_bitmap != tmp_bitmap_1)
1469 FreeBitmap(old_bitmap);
1472 UPDATE_BUSY_STATE();
1474 print_timestamp_done("CreateScaledBitmaps");
1477 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1480 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1483 void CreateBitmapTextures(Bitmap **bitmaps)
1485 if (bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL)
1486 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_PTR_ORIGINAL]);
1488 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1491 void FreeBitmapTextures(Bitmap **bitmaps)
1493 if (bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL)
1494 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_PTR_ORIGINAL]);
1496 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1499 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1501 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1505 // ----------------------------------------------------------------------------
1506 // mouse pointer functions
1507 // ----------------------------------------------------------------------------
1509 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1511 // XPM image definitions
1512 static const char *cursor_image_none[] =
1514 // width height num_colors chars_per_pixel
1544 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1545 static const char *cursor_image_dot[] =
1547 // width height num_colors chars_per_pixel
1576 static const char **cursor_image_playfield = cursor_image_dot;
1578 // some people complained about a "white dot" on the screen and thought it
1579 // was a graphical error... OK, let's just remove the whole pointer :-)
1580 static const char **cursor_image_playfield = cursor_image_none;
1583 static const int cursor_bit_order = BIT_ORDER_MSB;
1585 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1587 struct MouseCursorInfo *cursor;
1588 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1589 int header_lines = 4;
1592 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1594 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1597 for (y = 0; y < cursor->width; y++)
1599 for (x = 0; x < cursor->height; x++)
1602 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1607 cursor->data[i] = cursor->mask[i] = 0;
1610 switch (image[header_lines + y][x])
1613 cursor->data[i] |= bit_mask;
1614 cursor->mask[i] |= bit_mask;
1618 cursor->mask[i] |= bit_mask;
1627 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1632 void SetMouseCursor(int mode)
1634 static struct MouseCursorInfo *cursor_none = NULL;
1635 static struct MouseCursorInfo *cursor_playfield = NULL;
1636 struct MouseCursorInfo *cursor_new;
1637 int mode_final = mode;
1639 if (cursor_none == NULL)
1640 cursor_none = get_cursor_from_image(cursor_image_none);
1642 if (cursor_playfield == NULL)
1643 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1645 if (gfx.cursor_mode_override != CURSOR_UNDEFINED)
1646 mode_final = gfx.cursor_mode_override;
1648 cursor_new = (mode_final == CURSOR_DEFAULT ? NULL :
1649 mode_final == CURSOR_NONE ? cursor_none :
1650 mode_final == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1652 SDLSetMouseCursor(cursor_new);
1654 gfx.cursor_mode = mode;
1655 gfx.cursor_mode_final = mode_final;
1658 void UpdateRawMousePosition(int mouse_x, int mouse_y)
1660 // mouse events do not contain logical screen size corrections yet
1661 SDLCorrectRawMousePosition(&mouse_x, &mouse_y);
1663 mouse_x -= video.screen_xoffset;
1664 mouse_y -= video.screen_yoffset;
1666 gfx.mouse_x = mouse_x;
1667 gfx.mouse_y = mouse_y;
1670 void UpdateMousePosition(void)
1672 int mouse_x, mouse_y;
1675 SDL_GetMouseState(&mouse_x, &mouse_y);
1677 UpdateRawMousePosition(mouse_x, mouse_y);
1681 // ============================================================================
1683 // ============================================================================
1685 void OpenAudio(void)
1687 // always start with reliable default values
1688 audio.sound_available = FALSE;
1689 audio.music_available = FALSE;
1690 audio.loops_available = FALSE;
1692 audio.sound_enabled = FALSE;
1693 audio.sound_deactivated = FALSE;
1695 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1696 audio.mixer_pid = 0;
1697 audio.device_name = NULL;
1698 audio.device_fd = -1;
1700 audio.num_channels = 0;
1701 audio.music_channel = 0;
1702 audio.first_sound_channel = 0;
1707 void CloseAudio(void)
1711 audio.sound_enabled = FALSE;
1714 void SetAudioMode(boolean enabled)
1716 if (!audio.sound_available)
1719 audio.sound_enabled = enabled;
1723 // ============================================================================
1725 // ============================================================================
1727 void InitEventFilter(EventFilter filter_function)
1729 SDL_SetEventFilter(filter_function, NULL);
1732 boolean PendingEvent(void)
1734 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1737 void WaitEvent(Event *event)
1739 SDLWaitEvent(event);
1742 void PeekEvent(Event *event)
1744 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1747 void PumpEvents(void)
1752 void CheckQuitEvent(void)
1754 if (SDL_QuitRequested())
1755 program.exit_function(0);
1758 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1760 // key up/down events in SDL2 do not return text characters anymore
1761 return event->keysym.sym;
1764 KeyMod HandleKeyModState(Key key, int key_status)
1766 static KeyMod current_modifiers = KMOD_None;
1768 if (key != KSYM_UNDEFINED) // new key => check for modifier key change
1770 KeyMod new_modifier = KMOD_None;
1775 new_modifier = KMOD_Shift_L;
1778 new_modifier = KMOD_Shift_R;
1780 case KSYM_Control_L:
1781 new_modifier = KMOD_Control_L;
1783 case KSYM_Control_R:
1784 new_modifier = KMOD_Control_R;
1787 new_modifier = KMOD_Meta_L;
1790 new_modifier = KMOD_Meta_R;
1793 new_modifier = KMOD_Alt_L;
1796 new_modifier = KMOD_Alt_R;
1802 if (key_status == KEY_PRESSED)
1803 current_modifiers |= new_modifier;
1805 current_modifiers &= ~new_modifier;
1808 return current_modifiers;
1811 KeyMod GetKeyModState(void)
1813 return (KeyMod)SDL_GetModState();
1816 KeyMod GetKeyModStateFromEvents(void)
1818 /* always use key modifier state as tracked from key events (this is needed
1819 if the modifier key event was injected into the event queue, but the key
1820 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1821 query the keys as held pressed on the keyboard) -- this case is currently
1822 only used to filter out clipboard insert events from "True X-Mouse" tool */
1824 return HandleKeyModState(KSYM_UNDEFINED, 0);
1827 void StartTextInput(int x, int y, int width, int height)
1829 textinput_status = TRUE;
1831 #if defined(HAS_SCREEN_KEYBOARD)
1832 SDL_StartTextInput();
1834 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1836 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1837 video.shifted_up_delay = SDL_GetTicks();
1838 video.shifted_up = TRUE;
1843 void StopTextInput(void)
1845 textinput_status = FALSE;
1847 #if defined(HAS_SCREEN_KEYBOARD)
1848 SDL_StopTextInput();
1850 if (video.shifted_up)
1852 video.shifted_up_pos = 0;
1853 video.shifted_up_delay = SDL_GetTicks();
1854 video.shifted_up = FALSE;
1859 void PushUserEvent(int code, int value1, int value2)
1863 SDL_memset(&event, 0, sizeof(event));
1865 event.type = EVENT_USER;
1867 event.value1 = value1;
1868 event.value2 = value2;
1870 SDL_PushEvent((SDL_Event *)&event);
1874 // ============================================================================
1875 // joystick functions
1876 // ============================================================================
1878 void InitJoysticks(void)
1882 #if defined(NO_JOYSTICK)
1883 return; // joysticks generally deactivated by compile-time directive
1886 // always start with reliable default values
1887 joystick.status = JOYSTICK_NOT_AVAILABLE;
1888 for (i = 0; i < MAX_PLAYERS; i++)
1889 joystick.nr[i] = -1; // no joystick configured
1894 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1896 return SDLReadJoystick(nr, x, y, b1, b2);
1899 boolean CheckJoystickOpened(int nr)
1901 return SDLCheckJoystickOpened(nr);
1904 void ClearJoystickState(void)
1906 SDLClearJoystickState();
1910 // ============================================================================
1911 // Emscripten functions
1912 // ============================================================================
1914 void InitEmscriptenFilesystem(void)
1916 #if defined(PLATFORM_EMSCRIPTEN)
1919 Module.sync_done = 0;
1921 FS.mkdir('/persistent'); // create persistent data directory
1922 FS.mount(IDBFS, {}, '/persistent'); // mount with IDBFS filesystem type
1923 FS.syncfs(true, function(err) // sync persistent data into memory
1926 Module.sync_done = 1;
1930 // wait for persistent data to be synchronized to memory
1931 while (emscripten_run_script_int("Module.sync_done") == 0)
1936 void SyncEmscriptenFilesystem(void)
1938 #if defined(PLATFORM_EMSCRIPTEN)
1941 FS.syncfs(function(err)