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;
132 void InitScoresInfo(void)
134 char *global_scores_dir = getPath2(getCommonDataDir(), SCORES_DIRECTORY);
136 program.global_scores = directoryExists(global_scores_dir);
137 program.many_scores_per_name = !program.global_scores;
142 if (program.global_scores)
144 Debug("internal:path", "Using global, multi-user scores directory '%s'.",
146 Debug("internal:path", "Remove to enable single-user scores directory.");
147 Debug("internal:path", "(This enables multipe score entries per user.)");
151 Debug("internal:path", "Using private, single-user scores directory.");
156 free(global_scores_dir);
159 void SetWindowTitle(void)
161 program.window_title = program.window_title_function();
166 void InitWindowTitleFunction(char *(*window_title_function)(void))
168 program.window_title_function = window_title_function;
171 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
173 program.exit_message_function = exit_message_function;
176 void InitExitFunction(void (*exit_function)(int))
178 program.exit_function = exit_function;
180 // set signal handlers to custom exit function
181 // signal(SIGINT, exit_function);
182 signal(SIGTERM, exit_function);
184 // set exit function to automatically cleanup SDL stuff after exit()
188 void InitPlatformDependentStuff(void)
190 InitEmscriptenFilesystem();
192 // this is initialized in GetOptions(), but may already be used before
193 options.verbose = TRUE;
197 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
199 if (SDL_Init(sdl_init_flags) < 0)
200 Fail("SDL_Init() failed: %s", SDL_GetError());
205 void ClosePlatformDependentStuff(void)
210 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
211 int real_sx, int real_sy,
212 int full_sxsize, int full_sysize,
213 Bitmap *field_save_buffer)
219 gfx.real_sx = real_sx;
220 gfx.real_sy = real_sy;
221 gfx.full_sxsize = full_sxsize;
222 gfx.full_sysize = full_sysize;
224 gfx.field_save_buffer = field_save_buffer;
226 SetDrawDeactivationMask(REDRAW_NONE); // do not deactivate drawing
227 SetDrawBackgroundMask(REDRAW_NONE); // deactivate masked drawing
230 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
232 gfx.game_tile_size = game_tile_size;
233 gfx.standard_tile_size = standard_tile_size;
236 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
244 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
252 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
260 void InitGfxWindowInfo(int win_xsize, int win_ysize)
262 if (win_xsize != gfx.win_xsize || win_ysize != gfx.win_ysize)
264 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize);
266 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize);
268 ReCreateBitmap(&gfx.fade_bitmap_backup, win_xsize, win_ysize);
269 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize);
270 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize);
271 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize);
273 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
276 gfx.win_xsize = win_xsize;
277 gfx.win_ysize = win_ysize;
279 gfx.background_bitmap_mask = REDRAW_NONE;
282 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
284 // currently only used by MSDOS code to alloc VRAM buffer, if available
285 // 2009-03-24: also (temporarily?) used for overlapping blit workaround
286 gfx.scrollbuffer_width = scrollbuffer_width;
287 gfx.scrollbuffer_height = scrollbuffer_height;
290 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
292 gfx.clipping_enabled = enabled;
295 gfx.clip_width = width;
296 gfx.clip_height = height;
299 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
301 gfx.draw_busy_anim_function = draw_busy_anim_function;
304 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int))
306 gfx.draw_global_anim_function = draw_global_anim_function;
309 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
311 gfx.draw_global_border_function = draw_global_border_function;
314 void InitGfxDrawTileCursorFunction(void (*draw_tile_cursor_function)(int))
316 gfx.draw_tile_cursor_function = draw_tile_cursor_function;
319 void InitGfxCustomArtworkInfo(void)
321 gfx.override_level_graphics = FALSE;
322 gfx.override_level_sounds = FALSE;
323 gfx.override_level_music = FALSE;
325 gfx.draw_init_text = TRUE;
328 void InitGfxOtherSettings(void)
330 gfx.cursor_mode = CURSOR_DEFAULT;
331 gfx.cursor_mode_override = CURSOR_UNDEFINED;
332 gfx.cursor_mode_final = gfx.cursor_mode;
334 // prevent initially displaying custom mouse cursor in upper left corner
335 gfx.mouse_x = POS_OFFSCREEN;
336 gfx.mouse_y = POS_OFFSCREEN;
339 void InitTileCursorInfo(void)
341 tile_cursor.enabled = FALSE;
342 tile_cursor.active = FALSE;
343 tile_cursor.moving = FALSE;
345 tile_cursor.xpos = 0;
346 tile_cursor.ypos = 0;
349 tile_cursor.target_x = 0;
350 tile_cursor.target_y = 0;
355 tile_cursor.xsn_debug = FALSE;
358 void InitOverlayInfo(void)
360 overlay.enabled = FALSE;
361 overlay.active = FALSE;
363 overlay.show_grid = FALSE;
365 overlay.grid_button_highlight = CHAR_GRID_BUTTON_NONE;
366 overlay.grid_button_action = JOY_NO_ACTION;
368 SetOverlayGridSizeAndButtons();
370 #if defined(USE_TOUCH_INPUT_OVERLAY)
371 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
372 overlay.enabled = TRUE;
376 void SetOverlayGridSizeAndButtons(void)
378 int nr = GRID_ACTIVE_NR();
381 overlay.grid_xsize = setup.touch.grid_xsize[nr];
382 overlay.grid_ysize = setup.touch.grid_ysize[nr];
384 for (x = 0; x < MAX_GRID_XSIZE; x++)
385 for (y = 0; y < MAX_GRID_YSIZE; y++)
386 overlay.grid_button[x][y] = setup.touch.grid_button[nr][x][y];
389 void SetTileCursorEnabled(boolean enabled)
391 tile_cursor.enabled = enabled;
394 void SetTileCursorActive(boolean active)
396 tile_cursor.active = active;
399 void SetTileCursorTargetXY(int x, int y)
401 // delayed placement of tile selection cursor at target position
402 // (tile cursor will be moved to target position step by step)
404 tile_cursor.xpos = x;
405 tile_cursor.ypos = y;
406 tile_cursor.target_x = tile_cursor.sx + x * gfx.game_tile_size;
407 tile_cursor.target_y = tile_cursor.sy + y * gfx.game_tile_size;
409 tile_cursor.moving = TRUE;
412 void SetTileCursorXY(int x, int y)
414 // immediate placement of tile selection cursor at target position
416 SetTileCursorTargetXY(x, y);
418 tile_cursor.x = tile_cursor.target_x;
419 tile_cursor.y = tile_cursor.target_y;
421 tile_cursor.moving = FALSE;
424 void SetTileCursorSXSY(int sx, int sy)
430 void SetOverlayEnabled(boolean enabled)
432 overlay.enabled = enabled;
435 void SetOverlayActive(boolean active)
437 overlay.active = active;
440 void SetOverlayShowGrid(boolean show_grid)
442 overlay.show_grid = show_grid;
444 SetOverlayActive(show_grid);
447 SetOverlayEnabled(TRUE);
450 boolean GetOverlayEnabled(void)
452 return overlay.enabled;
455 boolean GetOverlayActive(void)
457 return overlay.active;
460 void SetDrawDeactivationMask(int draw_deactivation_mask)
462 gfx.draw_deactivation_mask = draw_deactivation_mask;
465 int GetDrawDeactivationMask(void)
467 return gfx.draw_deactivation_mask;
470 void SetDrawBackgroundMask(int draw_background_mask)
472 gfx.draw_background_mask = draw_background_mask;
475 static void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
477 if (background_bitmap_tile != NULL)
478 gfx.background_bitmap_mask |= mask;
480 gfx.background_bitmap_mask &= ~mask;
482 if (background_bitmap_tile == NULL) // empty background requested
485 if (mask == REDRAW_ALL)
486 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
487 0, 0, video.width, video.height);
488 else if (mask == REDRAW_FIELD)
489 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
490 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
491 else if (mask == REDRAW_DOOR_1)
492 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
493 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
496 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
498 // remove every mask before setting mask for window
499 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
500 SetBackgroundBitmap(NULL, 0xffff); // !!! FIX THIS !!!
501 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
504 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
506 // remove window area mask before setting mask for main area
507 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
508 SetBackgroundBitmap(NULL, REDRAW_ALL); // !!! FIX THIS !!!
509 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
512 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
514 // remove window area mask before setting mask for door area
515 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
516 SetBackgroundBitmap(NULL, REDRAW_ALL); // !!! FIX THIS !!!
517 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
521 // ============================================================================
523 // ============================================================================
525 static int GetRealDepth(int depth)
527 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
530 static void sysFillRectangle(Bitmap *bitmap, int x, int y,
531 int width, int height, Pixel color)
533 SDLFillRectangle(bitmap, x, y, width, height, color);
535 if (bitmap == backbuffer)
536 SetRedrawMaskFromArea(x, y, width, height);
539 static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
540 int src_x, int src_y, int width, int height,
541 int dst_x, int dst_y, int mask_mode)
543 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
544 dst_x, dst_y, mask_mode);
546 if (dst_bitmap == backbuffer)
547 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
550 void LimitScreenUpdates(boolean enable)
552 SDLLimitScreenUpdates(enable);
555 void InitVideoDefaults(void)
557 video.default_depth = 32;
560 void InitVideoDisplay(void)
562 if (program.headless)
565 SDLInitVideoDisplay();
569 void CloseVideoDisplay(void)
571 KeyboardAutoRepeatOn();
573 SDL_QuitSubSystem(SDL_INIT_VIDEO);
576 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
579 video.height = height;
580 video.depth = GetRealDepth(depth);
582 video.screen_width = width;
583 video.screen_height = height;
584 video.screen_xoffset = 0;
585 video.screen_yoffset = 0;
587 video.fullscreen_available = FULLSCREEN_STATUS;
588 video.fullscreen_enabled = FALSE;
590 video.window_scaling_available = WINDOW_SCALING_STATUS;
592 video.frame_counter = 0;
593 video.frame_delay = 0;
594 video.frame_delay_value = GAME_FRAME_DELAY;
596 video.shifted_up = FALSE;
597 video.shifted_up_pos = 0;
598 video.shifted_up_pos_last = 0;
599 video.shifted_up_delay = 0;
600 video.shifted_up_delay_value = ONE_SECOND_DELAY / 4;
602 SDLInitVideoBuffer(fullscreen);
604 video.initialized = !program.headless;
609 static void FreeBitmapPointers(Bitmap *bitmap)
614 SDLFreeBitmapPointers(bitmap);
616 checked_free(bitmap->source_filename);
617 bitmap->source_filename = NULL;
620 static void TransferBitmapPointers(Bitmap *src_bitmap,
623 if (src_bitmap == NULL || dst_bitmap == NULL)
626 FreeBitmapPointers(dst_bitmap);
628 *dst_bitmap = *src_bitmap;
631 void FreeBitmap(Bitmap *bitmap)
636 FreeBitmapPointers(bitmap);
641 Bitmap *CreateBitmapStruct(void)
643 return checked_calloc(sizeof(Bitmap));
646 Bitmap *CreateBitmap(int width, int height, int depth)
648 Bitmap *new_bitmap = CreateBitmapStruct();
649 int real_width = MAX(1, width); // prevent zero bitmap width
650 int real_height = MAX(1, height); // prevent zero bitmap height
651 int real_depth = GetRealDepth(depth);
653 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
655 new_bitmap->width = real_width;
656 new_bitmap->height = real_height;
661 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
665 // if new bitmap size fits into old one, no need to re-create it
666 if (width <= (*bitmap)->width &&
667 height <= (*bitmap)->height)
670 // else adjust size so that old and new bitmap size fit into it
671 width = MAX(width, (*bitmap)->width);
672 height = MAX(height, (*bitmap)->height);
675 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
679 *bitmap = new_bitmap;
683 TransferBitmapPointers(new_bitmap, *bitmap);
689 static void CloseWindow(DrawWindow *window)
694 void SetRedrawMaskFromArea(int x, int y, int width, int height)
698 int x2 = x + width - 1;
699 int y2 = y + height - 1;
701 if (width == 0 || height == 0)
704 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
705 redraw_mask |= REDRAW_FIELD;
706 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
707 redraw_mask |= REDRAW_DOOR_1;
708 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
709 redraw_mask |= REDRAW_DOOR_2;
710 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
711 redraw_mask |= REDRAW_DOOR_3;
713 redraw_mask = REDRAW_ALL;
716 static boolean CheckDrawingArea(int x, int y, int width, int height,
719 if (draw_mask == REDRAW_NONE)
722 if (draw_mask & REDRAW_ALL)
725 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
728 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
731 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
734 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
740 boolean DrawingDeactivatedField(void)
742 if (program.headless)
745 if (gfx.draw_deactivation_mask & REDRAW_FIELD)
751 boolean DrawingDeactivated(int x, int y, int width, int height)
753 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
756 boolean DrawingOnBackground(int x, int y)
758 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
759 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
762 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
763 int *width, int *height, boolean is_dest)
765 int clip_x, clip_y, clip_width, clip_height;
767 if (gfx.clipping_enabled && is_dest) // only clip destination bitmap
769 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
770 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
771 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
772 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
778 clip_width = bitmap->width;
779 clip_height = bitmap->height;
782 // skip if rectangle completely outside bitmap
784 if (*x + *width <= clip_x ||
785 *y + *height <= clip_y ||
786 *x >= clip_x + clip_width ||
787 *y >= clip_y + clip_height)
790 // clip if rectangle overlaps bitmap
794 *width -= clip_x - *x;
797 else if (*x + *width > clip_x + clip_width)
799 *width = clip_x + clip_width - *x;
804 *height -= clip_y - *y;
807 else if (*y + *height > clip_y + clip_height)
809 *height = clip_y + clip_height - *y;
815 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
816 int src_x, int src_y, int width, int height,
817 int dst_x, int dst_y)
819 int dst_x_unclipped = dst_x;
820 int dst_y_unclipped = dst_y;
822 if (program.headless)
825 if (src_bitmap == NULL || dst_bitmap == NULL)
828 if (DrawingDeactivated(dst_x, dst_y, width, height))
831 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
832 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
835 // source x/y might need adjustment if destination x/y was clipped top/left
836 src_x += dst_x - dst_x_unclipped;
837 src_y += dst_y - dst_y_unclipped;
839 // !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!!
840 // !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!!
841 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
842 but is already fixed in SVN and should therefore finally be fixed with
843 the next official SDL release, which is probably version 1.2.14.) */
844 // !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!!
846 if (src_bitmap == dst_bitmap)
848 // needed when blitting directly to same bitmap -- should not be needed with
849 // recent SDL libraries, but apparently does not work in 1.2.11 directly
851 static Bitmap *tmp_bitmap = NULL;
852 static int tmp_bitmap_xsize = 0;
853 static int tmp_bitmap_ysize = 0;
855 // start with largest static bitmaps for initial bitmap size ...
856 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
858 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
859 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
862 // ... and allow for later re-adjustments due to custom artwork bitmaps
863 if (src_bitmap->width > tmp_bitmap_xsize ||
864 src_bitmap->height > tmp_bitmap_ysize)
866 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
867 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
869 FreeBitmap(tmp_bitmap);
874 if (tmp_bitmap == NULL)
875 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
878 sysCopyArea(src_bitmap, tmp_bitmap,
879 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
880 sysCopyArea(tmp_bitmap, dst_bitmap,
881 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
886 sysCopyArea(src_bitmap, dst_bitmap,
887 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
890 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
891 int src_x, int src_y, int src_width, int src_height,
892 int dst_x, int dst_y, int dst_width, int dst_height)
894 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
895 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
896 int dst_xsize = dst_width;
897 int dst_ysize = dst_height;
898 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
899 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
902 for (y = 0; y < src_ysteps; y++)
904 for (x = 0; x < src_xsteps; x++)
906 int draw_x = dst_x + x * src_xsize;
907 int draw_y = dst_y + y * src_ysize;
908 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
909 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
911 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
917 void FadeRectangle(int x, int y, int width, int height,
918 int fade_mode, int fade_delay, int post_delay,
919 void (*draw_border_function)(void))
921 // (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined)
922 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
925 SDLFadeRectangle(x, y, width, height,
926 fade_mode, fade_delay, post_delay, draw_border_function);
929 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
932 if (DrawingDeactivated(x, y, width, height))
935 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
938 sysFillRectangle(bitmap, x, y, width, height, color);
941 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
943 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
946 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
947 int width, int height)
949 if (DrawingOnBackground(x, y))
950 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
952 ClearRectangle(bitmap, x, y, width, height);
955 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
956 int src_x, int src_y, int width, int height,
957 int dst_x, int dst_y)
959 if (DrawingDeactivated(dst_x, dst_y, width, height))
962 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
963 dst_x, dst_y, BLIT_MASKED);
966 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
967 int src_x, int src_y, int width, int height,
968 int dst_x, int dst_y)
970 if (DrawingOnBackground(dst_x, dst_y))
973 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
977 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
981 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
985 void BlitTexture(Bitmap *bitmap,
986 int src_x, int src_y, int width, int height,
987 int dst_x, int dst_y)
992 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
996 void BlitTextureMasked(Bitmap *bitmap,
997 int src_x, int src_y, int width, int height,
998 int dst_x, int dst_y)
1003 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
1007 void BlitToScreen(Bitmap *bitmap,
1008 int src_x, int src_y, int width, int height,
1009 int dst_x, int dst_y)
1014 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
1015 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
1016 width, height, dst_x, dst_y);
1018 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
1021 void BlitToScreenMasked(Bitmap *bitmap,
1022 int src_x, int src_y, int width, int height,
1023 int dst_x, int dst_y)
1028 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
1029 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
1030 width, height, dst_x, dst_y);
1032 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
1035 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
1038 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
1041 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
1044 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
1047 static void DrawLine(Bitmap *bitmap, int from_x, int from_y,
1048 int to_x, int to_y, Pixel pixel, int line_width)
1052 if (program.headless)
1055 for (x = 0; x < line_width; x++)
1057 for (y = 0; y < line_width; y++)
1059 int dx = x - line_width / 2;
1060 int dy = y - line_width / 2;
1062 if ((x == 0 && y == 0) ||
1063 (x == 0 && y == line_width - 1) ||
1064 (x == line_width - 1 && y == 0) ||
1065 (x == line_width - 1 && y == line_width - 1))
1069 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
1074 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
1079 for (i = 0; i < num_points - 1; i++)
1080 DrawLine(bitmap, points[i].x, points[i].y,
1081 points[i + 1].x, points[i + 1].y, pixel, line_width);
1084 SDLDrawLines(bitmap->surface, points, num_points, pixel);
1088 Pixel GetPixel(Bitmap *bitmap, int x, int y)
1090 if (program.headless)
1093 if (x < 0 || x >= bitmap->width ||
1094 y < 0 || y >= bitmap->height)
1097 return SDLGetPixel(bitmap, x, y);
1100 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
1101 unsigned int color_g, unsigned int color_b)
1103 if (program.headless)
1106 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
1109 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
1111 unsigned int color_r = (color >> 16) & 0xff;
1112 unsigned int color_g = (color >> 8) & 0xff;
1113 unsigned int color_b = (color >> 0) & 0xff;
1115 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
1118 void KeyboardAutoRepeatOn(void)
1120 keyrepeat_status = TRUE;
1123 void KeyboardAutoRepeatOff(void)
1125 keyrepeat_status = FALSE;
1128 boolean SetVideoMode(boolean fullscreen)
1130 return SDLSetVideoMode(fullscreen);
1133 void SetVideoFrameDelay(unsigned int frame_delay_value)
1135 video.frame_delay_value = frame_delay_value;
1138 unsigned int GetVideoFrameDelay(void)
1140 return video.frame_delay_value;
1143 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
1145 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
1146 (!fullscreen && video.fullscreen_enabled))
1147 fullscreen = SetVideoMode(fullscreen);
1152 Bitmap *LoadImage(char *filename)
1156 new_bitmap = SDLLoadImage(filename);
1159 new_bitmap->source_filename = getStringCopy(filename);
1164 Bitmap *LoadCustomImage(char *basename)
1166 char *filename = getCustomImageFilename(basename);
1169 if (filename == NULL)
1170 Fail("LoadCustomImage(): cannot find file '%s'", basename);
1172 if ((new_bitmap = LoadImage(filename)) == NULL)
1173 Fail("LoadImage('%s') failed", basename);
1178 void ReloadCustomImage(Bitmap *bitmap, char *basename)
1180 char *filename = getCustomImageFilename(basename);
1183 if (filename == NULL) // (should never happen)
1185 Warn("ReloadCustomImage(): cannot find file '%s'", basename);
1190 if (strEqual(filename, bitmap->source_filename))
1192 // The old and new image are the same (have the same filename and path).
1193 // This usually means that this image does not exist in this graphic set
1194 // and a fallback to the existing image is done.
1199 if ((new_bitmap = LoadImage(filename)) == NULL)
1201 Warn("LoadImage('%s') failed", basename);
1206 if (bitmap->width != new_bitmap->width ||
1207 bitmap->height != new_bitmap->height)
1209 Warn("ReloadCustomImage: new image '%s' has wrong dimensions",
1212 FreeBitmap(new_bitmap);
1217 TransferBitmapPointers(new_bitmap, bitmap);
1221 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1223 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
1226 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1228 if (bitmaps[IMG_BITMAP_CUSTOM])
1230 // check if original sized bitmap points to custom sized bitmap
1231 if (bitmaps[IMG_BITMAP_PTR_ORIGINAL] == bitmaps[IMG_BITMAP_CUSTOM])
1233 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_PTR_ORIGINAL]);
1235 // keep pointer of previous custom size bitmap
1236 bitmaps[IMG_BITMAP_OTHER] = bitmaps[IMG_BITMAP_CUSTOM];
1238 // set original bitmap pointer to scaled original bitmap of other size
1239 bitmaps[IMG_BITMAP_PTR_ORIGINAL] = bitmaps[IMG_BITMAP_OTHER];
1241 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_PTR_ORIGINAL]);
1245 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1248 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1251 if (gfx.game_tile_size == gfx.standard_tile_size)
1253 // set game bitmap pointer to standard sized bitmap (already existing)
1254 bitmaps[IMG_BITMAP_PTR_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1259 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1260 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1261 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1263 bitmaps[IMG_BITMAP_CUSTOM] = ZoomBitmap(bitmap, width, height);
1265 // set game bitmap pointer to custom sized bitmap (newly created)
1266 bitmaps[IMG_BITMAP_PTR_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1269 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1270 int tile_size, boolean create_small_bitmaps)
1272 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1273 Bitmap *tmp_bitmap_final = NULL;
1274 Bitmap *tmp_bitmap_0 = NULL;
1275 Bitmap *tmp_bitmap_1 = NULL;
1276 Bitmap *tmp_bitmap_2 = NULL;
1277 Bitmap *tmp_bitmap_4 = NULL;
1278 Bitmap *tmp_bitmap_8 = NULL;
1279 Bitmap *tmp_bitmap_16 = NULL;
1280 Bitmap *tmp_bitmap_32 = NULL;
1281 int width_final, height_final;
1282 int width_0, height_0;
1283 int width_1, height_1;
1284 int width_2, height_2;
1285 int width_4, height_4;
1286 int width_8, height_8;
1287 int width_16, height_16;
1288 int width_32, height_32;
1289 int old_width, old_height;
1292 print_timestamp_init("CreateScaledBitmaps");
1294 old_width = old_bitmap->width;
1295 old_height = old_bitmap->height;
1297 // calculate new image dimensions for final image size
1298 width_final = old_width * zoom_factor;
1299 height_final = old_height * zoom_factor;
1301 // get image with final size (this might require scaling up)
1302 // ("final" size may result in non-standard tile size image)
1303 if (zoom_factor != 1)
1304 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1306 tmp_bitmap_final = old_bitmap;
1308 UPDATE_BUSY_STATE();
1310 width_0 = width_1 = width_final;
1311 height_0 = height_1 = height_final;
1313 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1315 if (create_small_bitmaps)
1317 // check if we have a non-gameplay tile size image
1318 if (tile_size != gfx.game_tile_size)
1320 // get image with gameplay tile size
1321 width_0 = width_final * gfx.game_tile_size / tile_size;
1322 height_0 = height_final * gfx.game_tile_size / tile_size;
1324 if (width_0 == old_width)
1325 tmp_bitmap_0 = old_bitmap;
1326 else if (width_0 == width_final)
1327 tmp_bitmap_0 = tmp_bitmap_final;
1329 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1331 UPDATE_BUSY_STATE();
1334 // check if we have a non-standard tile size image
1335 if (tile_size != gfx.standard_tile_size)
1337 // get image with standard tile size
1338 width_1 = width_final * gfx.standard_tile_size / tile_size;
1339 height_1 = height_final * gfx.standard_tile_size / tile_size;
1341 if (width_1 == old_width)
1342 tmp_bitmap_1 = old_bitmap;
1343 else if (width_1 == width_final)
1344 tmp_bitmap_1 = tmp_bitmap_final;
1345 else if (width_1 == width_0)
1346 tmp_bitmap_1 = tmp_bitmap_0;
1348 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1350 UPDATE_BUSY_STATE();
1353 // calculate new image dimensions for small images
1354 width_2 = width_1 / 2;
1355 height_2 = height_1 / 2;
1356 width_4 = width_1 / 4;
1357 height_4 = height_1 / 4;
1358 width_8 = width_1 / 8;
1359 height_8 = height_1 / 8;
1360 width_16 = width_1 / 16;
1361 height_16 = height_1 / 16;
1362 width_32 = width_1 / 32;
1363 height_32 = height_1 / 32;
1365 // get image with 1/2 of normal size (for use in the level editor)
1366 if (width_2 == old_width)
1367 tmp_bitmap_2 = old_bitmap;
1369 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1371 UPDATE_BUSY_STATE();
1373 // get image with 1/4 of normal size (for use in the level editor)
1374 if (width_4 == old_width)
1375 tmp_bitmap_4 = old_bitmap;
1377 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1379 UPDATE_BUSY_STATE();
1381 // get image with 1/8 of normal size (for use on the preview screen)
1382 if (width_8 == old_width)
1383 tmp_bitmap_8 = old_bitmap;
1385 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1387 UPDATE_BUSY_STATE();
1389 // get image with 1/16 of normal size (for use on the preview screen)
1390 if (width_16 == old_width)
1391 tmp_bitmap_16 = old_bitmap;
1393 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1395 UPDATE_BUSY_STATE();
1397 // get image with 1/32 of normal size (for use on the preview screen)
1398 if (width_32 == old_width)
1399 tmp_bitmap_32 = old_bitmap;
1401 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1403 UPDATE_BUSY_STATE();
1405 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1406 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1407 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1408 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1409 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1410 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1412 if (width_0 != width_1)
1413 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1415 if (bitmaps[IMG_BITMAP_CUSTOM])
1416 bitmaps[IMG_BITMAP_PTR_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1418 bitmaps[IMG_BITMAP_PTR_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1420 // store the "final" (up-scaled) original bitmap, if not already stored
1422 int tmp_bitmap_final_nr = -1;
1424 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1425 if (bitmaps[i] == tmp_bitmap_final)
1426 tmp_bitmap_final_nr = i;
1428 if (tmp_bitmap_final_nr == -1) // scaled original bitmap not stored
1430 // store pointer of scaled original bitmap (not used for any other size)
1431 bitmaps[IMG_BITMAP_OTHER] = tmp_bitmap_final;
1433 // set original bitmap pointer to scaled original bitmap of other size
1434 bitmaps[IMG_BITMAP_PTR_ORIGINAL] = bitmaps[IMG_BITMAP_OTHER];
1438 // set original bitmap pointer to corresponding sized bitmap
1439 bitmaps[IMG_BITMAP_PTR_ORIGINAL] = bitmaps[tmp_bitmap_final_nr];
1442 // free the "old" (unscaled) original bitmap, if not already stored
1444 boolean free_old_bitmap = TRUE;
1446 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1447 if (bitmaps[i] == old_bitmap)
1448 free_old_bitmap = FALSE;
1450 if (free_old_bitmap)
1452 // copy image filename from old to new standard sized bitmap
1453 bitmaps[IMG_BITMAP_STANDARD]->source_filename =
1454 getStringCopy(old_bitmap->source_filename);
1456 FreeBitmap(old_bitmap);
1461 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1463 // set original bitmap pointer to corresponding sized bitmap
1464 bitmaps[IMG_BITMAP_PTR_ORIGINAL] = bitmaps[IMG_BITMAP_32x32];
1466 if (old_bitmap != tmp_bitmap_1)
1467 FreeBitmap(old_bitmap);
1470 UPDATE_BUSY_STATE();
1472 print_timestamp_done("CreateScaledBitmaps");
1475 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1478 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1481 void CreateBitmapTextures(Bitmap **bitmaps)
1483 if (bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL)
1484 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_PTR_ORIGINAL]);
1486 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1489 void FreeBitmapTextures(Bitmap **bitmaps)
1491 if (bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL)
1492 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_PTR_ORIGINAL]);
1494 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1497 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1499 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1503 // ----------------------------------------------------------------------------
1504 // mouse pointer functions
1505 // ----------------------------------------------------------------------------
1507 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1509 // XPM image definitions
1510 static const char *cursor_image_none[] =
1512 // width height num_colors chars_per_pixel
1542 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1543 static const char *cursor_image_dot[] =
1545 // width height num_colors chars_per_pixel
1574 static const char **cursor_image_playfield = cursor_image_dot;
1576 // some people complained about a "white dot" on the screen and thought it
1577 // was a graphical error... OK, let's just remove the whole pointer :-)
1578 static const char **cursor_image_playfield = cursor_image_none;
1581 static const int cursor_bit_order = BIT_ORDER_MSB;
1583 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1585 struct MouseCursorInfo *cursor;
1586 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1587 int header_lines = 4;
1590 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1592 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1595 for (y = 0; y < cursor->width; y++)
1597 for (x = 0; x < cursor->height; x++)
1600 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1605 cursor->data[i] = cursor->mask[i] = 0;
1608 switch (image[header_lines + y][x])
1611 cursor->data[i] |= bit_mask;
1612 cursor->mask[i] |= bit_mask;
1616 cursor->mask[i] |= bit_mask;
1625 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1630 void SetMouseCursor(int mode)
1632 static struct MouseCursorInfo *cursor_none = NULL;
1633 static struct MouseCursorInfo *cursor_playfield = NULL;
1634 struct MouseCursorInfo *cursor_new;
1635 int mode_final = mode;
1637 if (cursor_none == NULL)
1638 cursor_none = get_cursor_from_image(cursor_image_none);
1640 if (cursor_playfield == NULL)
1641 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1643 if (gfx.cursor_mode_override != CURSOR_UNDEFINED)
1644 mode_final = gfx.cursor_mode_override;
1646 cursor_new = (mode_final == CURSOR_DEFAULT ? NULL :
1647 mode_final == CURSOR_NONE ? cursor_none :
1648 mode_final == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1650 SDLSetMouseCursor(cursor_new);
1652 gfx.cursor_mode = mode;
1653 gfx.cursor_mode_final = mode_final;
1656 void UpdateRawMousePosition(int mouse_x, int mouse_y)
1658 // mouse events do not contain logical screen size corrections yet
1659 SDLCorrectRawMousePosition(&mouse_x, &mouse_y);
1661 mouse_x -= video.screen_xoffset;
1662 mouse_y -= video.screen_yoffset;
1664 gfx.mouse_x = mouse_x;
1665 gfx.mouse_y = mouse_y;
1668 void UpdateMousePosition(void)
1670 int mouse_x, mouse_y;
1673 SDL_GetMouseState(&mouse_x, &mouse_y);
1675 UpdateRawMousePosition(mouse_x, mouse_y);
1679 // ============================================================================
1681 // ============================================================================
1683 void OpenAudio(void)
1685 // always start with reliable default values
1686 audio.sound_available = FALSE;
1687 audio.music_available = FALSE;
1688 audio.loops_available = FALSE;
1690 audio.sound_enabled = FALSE;
1691 audio.sound_deactivated = FALSE;
1693 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1694 audio.mixer_pid = 0;
1695 audio.device_name = NULL;
1696 audio.device_fd = -1;
1698 audio.num_channels = 0;
1699 audio.music_channel = 0;
1700 audio.first_sound_channel = 0;
1705 void CloseAudio(void)
1709 audio.sound_enabled = FALSE;
1712 void SetAudioMode(boolean enabled)
1714 if (!audio.sound_available)
1717 audio.sound_enabled = enabled;
1721 // ============================================================================
1723 // ============================================================================
1725 void InitEventFilter(EventFilter filter_function)
1727 SDL_SetEventFilter(filter_function, NULL);
1730 boolean PendingEvent(void)
1732 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1735 void WaitEvent(Event *event)
1737 SDLWaitEvent(event);
1740 void PeekEvent(Event *event)
1742 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1745 void PumpEvents(void)
1750 void CheckQuitEvent(void)
1752 if (SDL_QuitRequested())
1753 program.exit_function(0);
1756 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1758 // key up/down events in SDL2 do not return text characters anymore
1759 return event->keysym.sym;
1762 KeyMod HandleKeyModState(Key key, int key_status)
1764 static KeyMod current_modifiers = KMOD_None;
1766 if (key != KSYM_UNDEFINED) // new key => check for modifier key change
1768 KeyMod new_modifier = KMOD_None;
1773 new_modifier = KMOD_Shift_L;
1776 new_modifier = KMOD_Shift_R;
1778 case KSYM_Control_L:
1779 new_modifier = KMOD_Control_L;
1781 case KSYM_Control_R:
1782 new_modifier = KMOD_Control_R;
1785 new_modifier = KMOD_Meta_L;
1788 new_modifier = KMOD_Meta_R;
1791 new_modifier = KMOD_Alt_L;
1794 new_modifier = KMOD_Alt_R;
1800 if (key_status == KEY_PRESSED)
1801 current_modifiers |= new_modifier;
1803 current_modifiers &= ~new_modifier;
1806 return current_modifiers;
1809 KeyMod GetKeyModState(void)
1811 return (KeyMod)SDL_GetModState();
1814 KeyMod GetKeyModStateFromEvents(void)
1816 /* always use key modifier state as tracked from key events (this is needed
1817 if the modifier key event was injected into the event queue, but the key
1818 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1819 query the keys as held pressed on the keyboard) -- this case is currently
1820 only used to filter out clipboard insert events from "True X-Mouse" tool */
1822 return HandleKeyModState(KSYM_UNDEFINED, 0);
1825 void StartTextInput(int x, int y, int width, int height)
1827 textinput_status = TRUE;
1829 #if defined(HAS_SCREEN_KEYBOARD)
1830 SDL_StartTextInput();
1832 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1834 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1835 video.shifted_up_delay = SDL_GetTicks();
1836 video.shifted_up = TRUE;
1841 void StopTextInput(void)
1843 textinput_status = FALSE;
1845 #if defined(HAS_SCREEN_KEYBOARD)
1846 SDL_StopTextInput();
1848 if (video.shifted_up)
1850 video.shifted_up_pos = 0;
1851 video.shifted_up_delay = SDL_GetTicks();
1852 video.shifted_up = FALSE;
1857 void PushUserEvent(int code, int value1, int value2)
1861 SDL_memset(&event, 0, sizeof(event));
1863 event.type = EVENT_USER;
1865 event.value1 = value1;
1866 event.value2 = value2;
1868 SDL_PushEvent((SDL_Event *)&event);
1872 // ============================================================================
1873 // joystick functions
1874 // ============================================================================
1876 void InitJoysticks(void)
1880 #if defined(NO_JOYSTICK)
1881 return; // joysticks generally deactivated by compile-time directive
1884 // always start with reliable default values
1885 joystick.status = JOYSTICK_NOT_AVAILABLE;
1886 for (i = 0; i < MAX_PLAYERS; i++)
1887 joystick.nr[i] = -1; // no joystick configured
1892 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1894 return SDLReadJoystick(nr, x, y, b1, b2);
1897 boolean CheckJoystickOpened(int nr)
1899 return SDLCheckJoystickOpened(nr);
1902 void ClearJoystickState(void)
1904 SDLClearJoystickState();
1908 // ============================================================================
1909 // Emscripten functions
1910 // ============================================================================
1912 void InitEmscriptenFilesystem(void)
1914 #if defined(PLATFORM_EMSCRIPTEN)
1917 Module.sync_done = 0;
1919 FS.mkdir('/persistent'); // create persistent data directory
1920 FS.mount(IDBFS, {}, '/persistent'); // mount with IDBFS filesystem type
1921 FS.syncfs(true, function(err) // sync persistent data into memory
1924 Module.sync_done = 1;
1928 // wait for persistent data to be synchronized to memory
1929 while (emscripten_run_script_int("Module.sync_done") == 0)
1934 void SyncEmscriptenFilesystem(void)
1936 #if defined(PLATFORM_EMSCRIPTEN)
1939 FS.syncfs(function(err)