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.api_thread_count = 0;
108 program.headless = FALSE;
111 void InitNetworkInfo(boolean enabled, boolean connected, boolean serveronly,
112 char *server_host, int server_port)
114 network.enabled = enabled;
115 network.connected = connected;
116 network.serveronly = serveronly;
118 network.server_host = server_host;
119 network.server_port = server_port;
121 network.server_thread = NULL;
122 network.is_server_thread = FALSE;
125 void InitRuntimeInfo()
127 #if defined(HAS_TOUCH_DEVICE)
128 runtime.uses_touch_device = TRUE;
130 runtime.uses_touch_device = FALSE;
133 runtime.use_api_server = setup.use_api_server;
136 void SetWindowTitle(void)
138 program.window_title = program.window_title_function();
143 void InitWindowTitleFunction(char *(*window_title_function)(void))
145 program.window_title_function = window_title_function;
148 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
150 program.exit_message_function = exit_message_function;
153 void InitExitFunction(void (*exit_function)(int))
155 program.exit_function = exit_function;
157 // set signal handlers to custom exit function
158 // signal(SIGINT, exit_function);
159 signal(SIGTERM, exit_function);
161 // set exit function to automatically cleanup SDL stuff after exit()
165 void InitPlatformDependentStuff(void)
167 InitEmscriptenFilesystem();
169 // this is initialized in GetOptions(), but may already be used before
170 options.verbose = TRUE;
174 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
176 if (SDL_Init(sdl_init_flags) < 0)
177 Fail("SDL_Init() failed: %s", SDL_GetError());
182 void ClosePlatformDependentStuff(void)
187 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
188 int real_sx, int real_sy,
189 int full_sxsize, int full_sysize,
190 Bitmap *field_save_buffer)
196 gfx.real_sx = real_sx;
197 gfx.real_sy = real_sy;
198 gfx.full_sxsize = full_sxsize;
199 gfx.full_sysize = full_sysize;
201 gfx.field_save_buffer = field_save_buffer;
203 SetDrawDeactivationMask(REDRAW_NONE); // do not deactivate drawing
204 SetDrawBackgroundMask(REDRAW_NONE); // deactivate masked drawing
207 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
209 gfx.game_tile_size = game_tile_size;
210 gfx.standard_tile_size = standard_tile_size;
213 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
221 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
229 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
237 void InitGfxWindowInfo(int win_xsize, int win_ysize)
239 if (win_xsize != gfx.win_xsize || win_ysize != gfx.win_ysize)
241 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize);
243 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize);
245 ReCreateBitmap(&gfx.fade_bitmap_backup, win_xsize, win_ysize);
246 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize);
247 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize);
248 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize);
250 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
253 gfx.win_xsize = win_xsize;
254 gfx.win_ysize = win_ysize;
256 gfx.background_bitmap_mask = REDRAW_NONE;
259 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
261 // currently only used by MSDOS code to alloc VRAM buffer, if available
262 // 2009-03-24: also (temporarily?) used for overlapping blit workaround
263 gfx.scrollbuffer_width = scrollbuffer_width;
264 gfx.scrollbuffer_height = scrollbuffer_height;
267 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
269 gfx.clipping_enabled = enabled;
272 gfx.clip_width = width;
273 gfx.clip_height = height;
276 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(boolean))
278 gfx.draw_busy_anim_function = draw_busy_anim_function;
281 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int))
283 gfx.draw_global_anim_function = draw_global_anim_function;
286 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
288 gfx.draw_global_border_function = draw_global_border_function;
291 void InitGfxDrawTileCursorFunction(void (*draw_tile_cursor_function)(int))
293 gfx.draw_tile_cursor_function = draw_tile_cursor_function;
296 void InitGfxCustomArtworkInfo(void)
298 gfx.override_level_graphics = FALSE;
299 gfx.override_level_sounds = FALSE;
300 gfx.override_level_music = FALSE;
302 gfx.draw_init_text = TRUE;
305 void InitGfxOtherSettings(void)
307 gfx.cursor_mode = CURSOR_DEFAULT;
308 gfx.cursor_mode_override = CURSOR_UNDEFINED;
309 gfx.cursor_mode_final = gfx.cursor_mode;
311 // prevent initially displaying custom mouse cursor in upper left corner
312 gfx.mouse_x = POS_OFFSCREEN;
313 gfx.mouse_y = POS_OFFSCREEN;
316 void InitTileCursorInfo(void)
318 tile_cursor.enabled = FALSE;
319 tile_cursor.active = FALSE;
320 tile_cursor.moving = FALSE;
322 tile_cursor.xpos = 0;
323 tile_cursor.ypos = 0;
326 tile_cursor.target_x = 0;
327 tile_cursor.target_y = 0;
332 tile_cursor.xsn_debug = FALSE;
335 void InitOverlayInfo(void)
337 overlay.enabled = FALSE;
338 overlay.active = FALSE;
340 overlay.show_grid = FALSE;
342 overlay.grid_button_highlight = CHAR_GRID_BUTTON_NONE;
343 overlay.grid_button_action = JOY_NO_ACTION;
345 SetOverlayGridSizeAndButtons();
347 #if defined(USE_TOUCH_INPUT_OVERLAY)
348 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
349 overlay.enabled = TRUE;
353 void SetOverlayGridSizeAndButtons(void)
355 int nr = GRID_ACTIVE_NR();
358 overlay.grid_xsize = setup.touch.grid_xsize[nr];
359 overlay.grid_ysize = setup.touch.grid_ysize[nr];
361 for (x = 0; x < MAX_GRID_XSIZE; x++)
362 for (y = 0; y < MAX_GRID_YSIZE; y++)
363 overlay.grid_button[x][y] = setup.touch.grid_button[nr][x][y];
366 void SetTileCursorEnabled(boolean enabled)
368 tile_cursor.enabled = enabled;
371 void SetTileCursorActive(boolean active)
373 tile_cursor.active = active;
376 void SetTileCursorTargetXY(int x, int y)
378 // delayed placement of tile selection cursor at target position
379 // (tile cursor will be moved to target position step by step)
381 tile_cursor.xpos = x;
382 tile_cursor.ypos = y;
383 tile_cursor.target_x = tile_cursor.sx + x * gfx.game_tile_size;
384 tile_cursor.target_y = tile_cursor.sy + y * gfx.game_tile_size;
386 tile_cursor.moving = TRUE;
389 void SetTileCursorXY(int x, int y)
391 // immediate placement of tile selection cursor at target position
393 SetTileCursorTargetXY(x, y);
395 tile_cursor.x = tile_cursor.target_x;
396 tile_cursor.y = tile_cursor.target_y;
398 tile_cursor.moving = FALSE;
401 void SetTileCursorSXSY(int sx, int sy)
407 void SetOverlayEnabled(boolean enabled)
409 overlay.enabled = enabled;
412 void SetOverlayActive(boolean active)
414 overlay.active = active;
417 void SetOverlayShowGrid(boolean show_grid)
419 overlay.show_grid = show_grid;
421 SetOverlayActive(show_grid);
424 SetOverlayEnabled(TRUE);
427 boolean GetOverlayEnabled(void)
429 return overlay.enabled;
432 boolean GetOverlayActive(void)
434 return overlay.active;
437 void SetDrawDeactivationMask(int draw_deactivation_mask)
439 gfx.draw_deactivation_mask = draw_deactivation_mask;
442 int GetDrawDeactivationMask(void)
444 return gfx.draw_deactivation_mask;
447 void SetDrawBackgroundMask(int draw_background_mask)
449 gfx.draw_background_mask = draw_background_mask;
452 static void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
454 if (background_bitmap_tile != NULL)
455 gfx.background_bitmap_mask |= mask;
457 gfx.background_bitmap_mask &= ~mask;
459 if (background_bitmap_tile == NULL) // empty background requested
462 if (mask == REDRAW_ALL)
463 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
464 0, 0, video.width, video.height);
465 else if (mask == REDRAW_FIELD)
466 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
467 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
468 else if (mask == REDRAW_DOOR_1)
469 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
470 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
473 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
475 // remove every mask before setting mask for window
476 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
477 SetBackgroundBitmap(NULL, 0xffff); // !!! FIX THIS !!!
478 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
481 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
483 // remove window area mask before setting mask for main area
484 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
485 SetBackgroundBitmap(NULL, REDRAW_ALL); // !!! FIX THIS !!!
486 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
489 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
491 // remove window area mask before setting mask for door area
492 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
493 SetBackgroundBitmap(NULL, REDRAW_ALL); // !!! FIX THIS !!!
494 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
498 // ============================================================================
500 // ============================================================================
502 static int GetRealDepth(int depth)
504 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
507 static void sysFillRectangle(Bitmap *bitmap, int x, int y,
508 int width, int height, Pixel color)
510 SDLFillRectangle(bitmap, x, y, width, height, color);
512 if (bitmap == backbuffer)
513 SetRedrawMaskFromArea(x, y, width, height);
516 static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
517 int src_x, int src_y, int width, int height,
518 int dst_x, int dst_y, int mask_mode)
520 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
521 dst_x, dst_y, mask_mode);
523 if (dst_bitmap == backbuffer)
524 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
527 void LimitScreenUpdates(boolean enable)
529 SDLLimitScreenUpdates(enable);
532 void InitVideoDefaults(void)
534 video.default_depth = 32;
537 void InitVideoDisplay(void)
539 if (program.headless)
542 SDLInitVideoDisplay();
546 void CloseVideoDisplay(void)
548 KeyboardAutoRepeatOn();
550 SDL_QuitSubSystem(SDL_INIT_VIDEO);
553 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
556 video.height = height;
557 video.depth = GetRealDepth(depth);
559 video.screen_width = width;
560 video.screen_height = height;
561 video.screen_xoffset = 0;
562 video.screen_yoffset = 0;
564 video.fullscreen_available = FULLSCREEN_STATUS;
565 video.fullscreen_enabled = FALSE;
567 video.window_scaling_available = WINDOW_SCALING_STATUS;
569 video.frame_counter = 0;
570 video.frame_delay.count = 0;
571 video.frame_delay.value = GAME_FRAME_DELAY;
573 video.shifted_up = FALSE;
574 video.shifted_up_pos = 0;
575 video.shifted_up_pos_last = 0;
576 video.shifted_up_delay.count = 0;
577 video.shifted_up_delay.value = ONE_SECOND_DELAY / 4;
579 SDLInitVideoBuffer(fullscreen);
581 video.initialized = !program.headless;
586 static void FreeBitmapPointers(Bitmap *bitmap)
591 SDLFreeBitmapPointers(bitmap);
593 checked_free(bitmap->source_filename);
594 bitmap->source_filename = NULL;
597 static void TransferBitmapPointers(Bitmap *src_bitmap,
600 if (src_bitmap == NULL || dst_bitmap == NULL)
603 FreeBitmapPointers(dst_bitmap);
605 *dst_bitmap = *src_bitmap;
608 void FreeBitmap(Bitmap *bitmap)
613 FreeBitmapPointers(bitmap);
618 Bitmap *CreateBitmapStruct(void)
620 return checked_calloc(sizeof(Bitmap));
623 Bitmap *CreateBitmap(int width, int height, int depth)
625 Bitmap *new_bitmap = CreateBitmapStruct();
626 int real_width = MAX(1, width); // prevent zero bitmap width
627 int real_height = MAX(1, height); // prevent zero bitmap height
628 int real_depth = GetRealDepth(depth);
630 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
632 new_bitmap->width = real_width;
633 new_bitmap->height = real_height;
638 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
642 // if new bitmap size fits into old one, no need to re-create it
643 if (width <= (*bitmap)->width &&
644 height <= (*bitmap)->height)
647 // else adjust size so that old and new bitmap size fit into it
648 width = MAX(width, (*bitmap)->width);
649 height = MAX(height, (*bitmap)->height);
652 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
656 *bitmap = new_bitmap;
660 TransferBitmapPointers(new_bitmap, *bitmap);
666 static void CloseWindow(DrawWindow *window)
671 void SetRedrawMaskFromArea(int x, int y, int width, int height)
675 int x2 = x + width - 1;
676 int y2 = y + height - 1;
678 if (width == 0 || height == 0)
681 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
682 redraw_mask |= REDRAW_FIELD;
683 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
684 redraw_mask |= REDRAW_DOOR_1;
685 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
686 redraw_mask |= REDRAW_DOOR_2;
687 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
688 redraw_mask |= REDRAW_DOOR_3;
690 redraw_mask = REDRAW_ALL;
693 static boolean CheckDrawingArea(int x, int y, int width, int height,
696 if (draw_mask == REDRAW_NONE)
699 if (draw_mask & REDRAW_ALL)
702 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
705 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
708 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
711 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
717 boolean DrawingDeactivatedField(void)
719 if (program.headless)
722 if (gfx.draw_deactivation_mask & REDRAW_FIELD)
728 boolean DrawingDeactivated(int x, int y, int width, int height)
730 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
733 boolean DrawingOnBackground(int x, int y)
735 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
736 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
739 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
740 int *width, int *height, boolean is_dest)
742 int clip_x, clip_y, clip_width, clip_height;
744 if (gfx.clipping_enabled && is_dest) // only clip destination bitmap
746 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
747 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
748 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
749 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
755 clip_width = bitmap->width;
756 clip_height = bitmap->height;
759 // skip if rectangle completely outside bitmap
761 if (*x + *width <= clip_x ||
762 *y + *height <= clip_y ||
763 *x >= clip_x + clip_width ||
764 *y >= clip_y + clip_height)
767 // clip if rectangle overlaps bitmap
771 *width -= clip_x - *x;
774 else if (*x + *width > clip_x + clip_width)
776 *width = clip_x + clip_width - *x;
781 *height -= clip_y - *y;
784 else if (*y + *height > clip_y + clip_height)
786 *height = clip_y + clip_height - *y;
792 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
793 int src_x, int src_y, int width, int height,
794 int dst_x, int dst_y)
796 int dst_x_unclipped = dst_x;
797 int dst_y_unclipped = dst_y;
799 if (program.headless)
802 if (src_bitmap == NULL || dst_bitmap == NULL)
805 if (DrawingDeactivated(dst_x, dst_y, width, height))
808 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
809 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
812 // source x/y might need adjustment if destination x/y was clipped top/left
813 src_x += dst_x - dst_x_unclipped;
814 src_y += dst_y - dst_y_unclipped;
816 // !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!!
817 // !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!!
818 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
819 but is already fixed in SVN and should therefore finally be fixed with
820 the next official SDL release, which is probably version 1.2.14.) */
821 // !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!!
823 if (src_bitmap == dst_bitmap)
825 // needed when blitting directly to same bitmap -- should not be needed with
826 // recent SDL libraries, but apparently does not work in 1.2.11 directly
828 static Bitmap *tmp_bitmap = NULL;
829 static int tmp_bitmap_xsize = 0;
830 static int tmp_bitmap_ysize = 0;
832 // start with largest static bitmaps for initial bitmap size ...
833 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
835 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
836 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
839 // ... and allow for later re-adjustments due to custom artwork bitmaps
840 if (src_bitmap->width > tmp_bitmap_xsize ||
841 src_bitmap->height > tmp_bitmap_ysize)
843 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
844 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
846 FreeBitmap(tmp_bitmap);
851 if (tmp_bitmap == NULL)
852 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
855 sysCopyArea(src_bitmap, tmp_bitmap,
856 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
857 sysCopyArea(tmp_bitmap, dst_bitmap,
858 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
863 sysCopyArea(src_bitmap, dst_bitmap,
864 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
867 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
868 int src_x, int src_y, int src_width, int src_height,
869 int dst_x, int dst_y, int dst_width, int dst_height)
871 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
872 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
873 int dst_xsize = dst_width;
874 int dst_ysize = dst_height;
875 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
876 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
879 for (y = 0; y < src_ysteps; y++)
881 for (x = 0; x < src_xsteps; x++)
883 int draw_x = dst_x + x * src_xsize;
884 int draw_y = dst_y + y * src_ysize;
885 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
886 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
888 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
894 void FadeRectangle(int x, int y, int width, int height,
895 int fade_mode, int fade_delay, int post_delay,
896 void (*draw_border_function)(void))
898 // (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined)
899 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
902 SDLFadeRectangle(x, y, width, height,
903 fade_mode, fade_delay, post_delay, draw_border_function);
906 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
909 if (program.headless)
912 if (DrawingDeactivated(x, y, width, height))
915 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
918 sysFillRectangle(bitmap, x, y, width, height, color);
921 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
923 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
926 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
927 int width, int height)
929 if (DrawingOnBackground(x, y))
930 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
932 ClearRectangle(bitmap, x, y, width, height);
935 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
936 int src_x, int src_y, int width, int height,
937 int dst_x, int dst_y)
939 if (DrawingDeactivated(dst_x, dst_y, width, height))
942 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
943 dst_x, dst_y, BLIT_MASKED);
946 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
947 int src_x, int src_y, int width, int height,
948 int dst_x, int dst_y)
950 if (DrawingOnBackground(dst_x, dst_y))
953 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
957 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
961 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
965 void BlitTexture(Bitmap *bitmap,
966 int src_x, int src_y, int width, int height,
967 int dst_x, int dst_y)
972 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
976 void BlitTextureMasked(Bitmap *bitmap,
977 int src_x, int src_y, int width, int height,
978 int dst_x, int dst_y)
983 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
987 void BlitToScreen(Bitmap *bitmap,
988 int src_x, int src_y, int width, int height,
989 int dst_x, int dst_y)
994 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
995 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
996 width, height, dst_x, dst_y);
998 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
1001 void BlitToScreenMasked(Bitmap *bitmap,
1002 int src_x, int src_y, int width, int height,
1003 int dst_x, int dst_y)
1008 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
1009 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
1010 width, height, dst_x, dst_y);
1012 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
1015 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
1018 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
1021 static void DrawLine(Bitmap *bitmap, int from_x, int from_y,
1022 int to_x, int to_y, Pixel pixel, int line_width)
1026 if (program.headless)
1029 for (x = 0; x < line_width; x++)
1031 for (y = 0; y < line_width; y++)
1033 int dx = x - line_width / 2;
1034 int dy = y - line_width / 2;
1036 if ((x == 0 && y == 0) ||
1037 (x == 0 && y == line_width - 1) ||
1038 (x == line_width - 1 && y == 0) ||
1039 (x == line_width - 1 && y == line_width - 1))
1043 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
1048 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
1053 for (i = 0; i < num_points - 1; i++)
1054 DrawLine(bitmap, points[i].x, points[i].y,
1055 points[i + 1].x, points[i + 1].y, pixel, line_width);
1058 SDLDrawLines(bitmap->surface, points, num_points, pixel);
1062 Pixel GetPixel(Bitmap *bitmap, int x, int y)
1064 if (program.headless)
1067 if (x < 0 || x >= bitmap->width ||
1068 y < 0 || y >= bitmap->height)
1071 return SDLGetPixel(bitmap, x, y);
1074 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
1075 unsigned int color_g, unsigned int color_b)
1077 if (program.headless)
1080 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
1083 void KeyboardAutoRepeatOn(void)
1085 keyrepeat_status = TRUE;
1088 void KeyboardAutoRepeatOff(void)
1090 keyrepeat_status = FALSE;
1093 boolean SetVideoMode(boolean fullscreen)
1095 return SDLSetVideoMode(fullscreen);
1098 void SetVideoFrameDelay(unsigned int frame_delay_value)
1100 video.frame_delay.value = frame_delay_value;
1103 unsigned int GetVideoFrameDelay(void)
1105 return video.frame_delay.value;
1108 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
1110 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
1111 (!fullscreen && video.fullscreen_enabled))
1112 fullscreen = SetVideoMode(fullscreen);
1117 Bitmap *LoadImage(char *filename)
1121 new_bitmap = SDLLoadImage(filename);
1124 new_bitmap->source_filename = getStringCopy(filename);
1129 Bitmap *LoadCustomImage(char *basename)
1131 char *filename = getCustomImageFilename(basename);
1134 if (filename == NULL)
1135 Fail("LoadCustomImage(): cannot find file '%s'", basename);
1137 if ((new_bitmap = LoadImage(filename)) == NULL)
1138 Fail("LoadImage('%s') failed", basename);
1143 void ReloadCustomImage(Bitmap *bitmap, char *basename)
1145 char *filename = getCustomImageFilename(basename);
1148 if (filename == NULL) // (should never happen)
1150 Warn("ReloadCustomImage(): cannot find file '%s'", basename);
1155 if (strEqual(filename, bitmap->source_filename))
1157 // The old and new image are the same (have the same filename and path).
1158 // This usually means that this image does not exist in this graphic set
1159 // and a fallback to the existing image is done.
1164 if ((new_bitmap = LoadImage(filename)) == NULL)
1166 Warn("LoadImage('%s') failed", basename);
1171 if (bitmap->width != new_bitmap->width ||
1172 bitmap->height != new_bitmap->height)
1174 Warn("ReloadCustomImage: new image '%s' has wrong dimensions",
1177 FreeBitmap(new_bitmap);
1182 TransferBitmapPointers(new_bitmap, bitmap);
1186 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1188 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
1191 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1193 if (bitmaps[IMG_BITMAP_CUSTOM])
1195 // check if original sized bitmap points to custom sized bitmap
1196 if (bitmaps[IMG_BITMAP_PTR_ORIGINAL] == bitmaps[IMG_BITMAP_CUSTOM])
1198 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_PTR_ORIGINAL]);
1200 // keep pointer of previous custom size bitmap
1201 bitmaps[IMG_BITMAP_OTHER] = bitmaps[IMG_BITMAP_CUSTOM];
1203 // set original bitmap pointer to scaled original bitmap of other size
1204 bitmaps[IMG_BITMAP_PTR_ORIGINAL] = bitmaps[IMG_BITMAP_OTHER];
1206 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_PTR_ORIGINAL]);
1210 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1213 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1216 if (gfx.game_tile_size == gfx.standard_tile_size)
1218 // set game bitmap pointer to standard sized bitmap (already existing)
1219 bitmaps[IMG_BITMAP_PTR_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1224 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1225 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1226 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1228 bitmaps[IMG_BITMAP_CUSTOM] = ZoomBitmap(bitmap, width, height);
1230 // set game bitmap pointer to custom sized bitmap (newly created)
1231 bitmaps[IMG_BITMAP_PTR_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1234 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1235 int tile_size, boolean create_small_bitmaps)
1237 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1238 Bitmap *tmp_bitmap_final = NULL;
1239 Bitmap *tmp_bitmap_0 = NULL;
1240 Bitmap *tmp_bitmap_1 = NULL;
1241 Bitmap *tmp_bitmap_2 = NULL;
1242 Bitmap *tmp_bitmap_4 = NULL;
1243 Bitmap *tmp_bitmap_8 = NULL;
1244 Bitmap *tmp_bitmap_16 = NULL;
1245 Bitmap *tmp_bitmap_32 = NULL;
1246 int width_final, height_final;
1247 int width_0, height_0;
1248 int width_1, height_1;
1249 int width_2, height_2;
1250 int width_4, height_4;
1251 int width_8, height_8;
1252 int width_16, height_16;
1253 int width_32, height_32;
1254 int old_width, old_height;
1257 print_timestamp_init("CreateScaledBitmaps");
1259 old_width = old_bitmap->width;
1260 old_height = old_bitmap->height;
1262 // calculate new image dimensions for final image size
1263 width_final = old_width * zoom_factor;
1264 height_final = old_height * zoom_factor;
1266 // get image with final size (this might require scaling up)
1267 // ("final" size may result in non-standard tile size image)
1268 if (zoom_factor != 1)
1269 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1271 tmp_bitmap_final = old_bitmap;
1273 UPDATE_BUSY_STATE();
1275 width_0 = width_1 = width_final;
1276 height_0 = height_1 = height_final;
1278 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1280 if (create_small_bitmaps)
1282 // check if we have a non-gameplay tile size image
1283 if (tile_size != gfx.game_tile_size)
1285 // get image with gameplay tile size
1286 width_0 = width_final * gfx.game_tile_size / tile_size;
1287 height_0 = height_final * gfx.game_tile_size / tile_size;
1289 if (width_0 == old_width)
1290 tmp_bitmap_0 = old_bitmap;
1291 else if (width_0 == width_final)
1292 tmp_bitmap_0 = tmp_bitmap_final;
1294 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1296 UPDATE_BUSY_STATE();
1299 // check if we have a non-standard tile size image
1300 if (tile_size != gfx.standard_tile_size)
1302 // get image with standard tile size
1303 width_1 = width_final * gfx.standard_tile_size / tile_size;
1304 height_1 = height_final * gfx.standard_tile_size / tile_size;
1306 if (width_1 == old_width)
1307 tmp_bitmap_1 = old_bitmap;
1308 else if (width_1 == width_final)
1309 tmp_bitmap_1 = tmp_bitmap_final;
1310 else if (width_1 == width_0)
1311 tmp_bitmap_1 = tmp_bitmap_0;
1313 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1315 UPDATE_BUSY_STATE();
1318 // calculate new image dimensions for small images
1319 width_2 = width_1 / 2;
1320 height_2 = height_1 / 2;
1321 width_4 = width_1 / 4;
1322 height_4 = height_1 / 4;
1323 width_8 = width_1 / 8;
1324 height_8 = height_1 / 8;
1325 width_16 = width_1 / 16;
1326 height_16 = height_1 / 16;
1327 width_32 = width_1 / 32;
1328 height_32 = height_1 / 32;
1330 // get image with 1/2 of normal size (for use in the level editor)
1331 if (width_2 == old_width)
1332 tmp_bitmap_2 = old_bitmap;
1334 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1336 UPDATE_BUSY_STATE();
1338 // get image with 1/4 of normal size (for use in the level editor)
1339 if (width_4 == old_width)
1340 tmp_bitmap_4 = old_bitmap;
1342 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1344 UPDATE_BUSY_STATE();
1346 // get image with 1/8 of normal size (for use on the preview screen)
1347 if (width_8 == old_width)
1348 tmp_bitmap_8 = old_bitmap;
1350 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1352 UPDATE_BUSY_STATE();
1354 // get image with 1/16 of normal size (for use on the preview screen)
1355 if (width_16 == old_width)
1356 tmp_bitmap_16 = old_bitmap;
1358 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1360 UPDATE_BUSY_STATE();
1362 // get image with 1/32 of normal size (for use on the preview screen)
1363 if (width_32 == old_width)
1364 tmp_bitmap_32 = old_bitmap;
1366 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1368 UPDATE_BUSY_STATE();
1370 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1371 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1372 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1373 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1374 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1375 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1377 if (width_0 != width_1)
1378 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1380 if (bitmaps[IMG_BITMAP_CUSTOM])
1381 bitmaps[IMG_BITMAP_PTR_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1383 bitmaps[IMG_BITMAP_PTR_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1385 // store the "final" (up-scaled) original bitmap, if not already stored
1387 int tmp_bitmap_final_nr = -1;
1389 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1390 if (bitmaps[i] == tmp_bitmap_final)
1391 tmp_bitmap_final_nr = i;
1393 if (tmp_bitmap_final_nr == -1) // scaled original bitmap not stored
1395 // store pointer of scaled original bitmap (not used for any other size)
1396 bitmaps[IMG_BITMAP_OTHER] = tmp_bitmap_final;
1398 // set original bitmap pointer to scaled original bitmap of other size
1399 bitmaps[IMG_BITMAP_PTR_ORIGINAL] = bitmaps[IMG_BITMAP_OTHER];
1403 // set original bitmap pointer to corresponding sized bitmap
1404 bitmaps[IMG_BITMAP_PTR_ORIGINAL] = bitmaps[tmp_bitmap_final_nr];
1407 // free the "old" (unscaled) original bitmap, if not already stored
1409 boolean free_old_bitmap = TRUE;
1411 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1412 if (bitmaps[i] == old_bitmap)
1413 free_old_bitmap = FALSE;
1415 if (free_old_bitmap)
1417 // copy image filename from old to new standard sized bitmap
1418 bitmaps[IMG_BITMAP_STANDARD]->source_filename =
1419 getStringCopy(old_bitmap->source_filename);
1421 FreeBitmap(old_bitmap);
1426 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1428 // set original bitmap pointer to corresponding sized bitmap
1429 bitmaps[IMG_BITMAP_PTR_ORIGINAL] = bitmaps[IMG_BITMAP_32x32];
1431 if (old_bitmap != tmp_bitmap_1)
1432 FreeBitmap(old_bitmap);
1435 UPDATE_BUSY_STATE();
1437 print_timestamp_done("CreateScaledBitmaps");
1440 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1443 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1446 void CreateBitmapTextures(Bitmap **bitmaps)
1448 if (bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL)
1449 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_PTR_ORIGINAL]);
1451 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1454 void FreeBitmapTextures(Bitmap **bitmaps)
1456 if (bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL)
1457 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_PTR_ORIGINAL]);
1459 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1462 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1464 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1468 // ----------------------------------------------------------------------------
1469 // mouse pointer functions
1470 // ----------------------------------------------------------------------------
1472 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1474 // XPM image definitions
1475 static const char *cursor_image_none[] =
1477 // width height num_colors chars_per_pixel
1507 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1508 static const char *cursor_image_dot[] =
1510 // width height num_colors chars_per_pixel
1539 static const char **cursor_image_playfield = cursor_image_dot;
1541 // some people complained about a "white dot" on the screen and thought it
1542 // was a graphical error... OK, let's just remove the whole pointer :-)
1543 static const char **cursor_image_playfield = cursor_image_none;
1546 static const int cursor_bit_order = BIT_ORDER_MSB;
1548 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1550 struct MouseCursorInfo *cursor;
1551 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1552 int header_lines = 4;
1555 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1557 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1560 for (y = 0; y < cursor->width; y++)
1562 for (x = 0; x < cursor->height; x++)
1565 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1570 cursor->data[i] = cursor->mask[i] = 0;
1573 switch (image[header_lines + y][x])
1576 cursor->data[i] |= bit_mask;
1577 cursor->mask[i] |= bit_mask;
1581 cursor->mask[i] |= bit_mask;
1590 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1595 void SetMouseCursor(int mode)
1597 static struct MouseCursorInfo *cursor_none = NULL;
1598 static struct MouseCursorInfo *cursor_playfield = NULL;
1599 struct MouseCursorInfo *cursor_new;
1600 int mode_final = mode;
1602 if (cursor_none == NULL)
1603 cursor_none = get_cursor_from_image(cursor_image_none);
1605 if (cursor_playfield == NULL)
1606 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1608 if (gfx.cursor_mode_override != CURSOR_UNDEFINED)
1609 mode_final = gfx.cursor_mode_override;
1611 cursor_new = (mode_final == CURSOR_DEFAULT ? NULL :
1612 mode_final == CURSOR_NONE ? cursor_none :
1613 mode_final == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1615 SDLSetMouseCursor(cursor_new);
1617 gfx.cursor_mode = mode;
1618 gfx.cursor_mode_final = mode_final;
1621 void UpdateRawMousePosition(int mouse_x, int mouse_y)
1623 // mouse events do not contain logical screen size corrections yet
1624 SDLCorrectRawMousePosition(&mouse_x, &mouse_y);
1626 mouse_x -= video.screen_xoffset;
1627 mouse_y -= video.screen_yoffset;
1629 gfx.mouse_x = mouse_x;
1630 gfx.mouse_y = mouse_y;
1633 void UpdateMousePosition(void)
1635 int mouse_x, mouse_y;
1638 SDL_GetMouseState(&mouse_x, &mouse_y);
1640 UpdateRawMousePosition(mouse_x, mouse_y);
1644 // ============================================================================
1646 // ============================================================================
1648 void OpenAudio(void)
1650 // always start with reliable default values
1651 audio.sound_available = FALSE;
1652 audio.music_available = FALSE;
1653 audio.loops_available = FALSE;
1655 audio.sound_enabled = FALSE;
1656 audio.sound_deactivated = FALSE;
1658 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1659 audio.mixer_pid = 0;
1660 audio.device_name = NULL;
1661 audio.device_fd = -1;
1663 audio.num_channels = 0;
1664 audio.music_channel = 0;
1665 audio.first_sound_channel = 0;
1670 void CloseAudio(void)
1674 audio.sound_enabled = FALSE;
1677 void SetAudioMode(boolean enabled)
1679 if (!audio.sound_available)
1682 audio.sound_enabled = enabled;
1686 // ============================================================================
1688 // ============================================================================
1690 void InitEventFilter(EventFilter filter_function)
1692 SDL_SetEventFilter(filter_function, NULL);
1695 boolean PendingEvent(void)
1697 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1700 void WaitEvent(Event *event)
1702 SDLWaitEvent(event);
1705 void PeekEvent(Event *event)
1707 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1710 void PumpEvents(void)
1715 void CheckQuitEvent(void)
1717 if (SDL_QuitRequested())
1718 program.exit_function(0);
1721 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1723 // key up/down events in SDL2 do not return text characters anymore
1724 return event->keysym.sym;
1727 KeyMod HandleKeyModState(Key key, int key_status)
1729 static KeyMod current_modifiers = KMOD_None;
1731 if (key != KSYM_UNDEFINED) // new key => check for modifier key change
1733 KeyMod new_modifier = KMOD_None;
1738 new_modifier = KMOD_Shift_L;
1741 new_modifier = KMOD_Shift_R;
1743 case KSYM_Control_L:
1744 new_modifier = KMOD_Control_L;
1746 case KSYM_Control_R:
1747 new_modifier = KMOD_Control_R;
1750 new_modifier = KMOD_Meta_L;
1753 new_modifier = KMOD_Meta_R;
1756 new_modifier = KMOD_Alt_L;
1759 new_modifier = KMOD_Alt_R;
1765 if (key_status == KEY_PRESSED)
1766 current_modifiers |= new_modifier;
1768 current_modifiers &= ~new_modifier;
1771 return current_modifiers;
1774 KeyMod GetKeyModState(void)
1776 return (KeyMod)SDL_GetModState();
1779 KeyMod GetKeyModStateFromEvents(void)
1781 /* always use key modifier state as tracked from key events (this is needed
1782 if the modifier key event was injected into the event queue, but the key
1783 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1784 query the keys as held pressed on the keyboard) -- this case is currently
1785 only used to filter out clipboard insert events from "True X-Mouse" tool */
1787 return HandleKeyModState(KSYM_UNDEFINED, 0);
1790 void StartTextInput(int x, int y, int width, int height)
1792 textinput_status = TRUE;
1794 #if defined(HAS_SCREEN_KEYBOARD)
1795 SDL_StartTextInput();
1797 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1799 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1800 video.shifted_up_delay.count = SDL_GetTicks();
1801 video.shifted_up = TRUE;
1806 void StopTextInput(void)
1808 textinput_status = FALSE;
1810 #if defined(HAS_SCREEN_KEYBOARD)
1811 SDL_StopTextInput();
1813 if (video.shifted_up)
1815 video.shifted_up_pos = 0;
1816 video.shifted_up_delay.count = SDL_GetTicks();
1817 video.shifted_up = FALSE;
1822 void PushUserEvent(int code, int value1, int value2)
1826 SDL_memset(&event, 0, sizeof(event));
1828 event.type = EVENT_USER;
1830 event.value1 = value1;
1831 event.value2 = value2;
1833 SDL_PushEvent((SDL_Event *)&event);
1836 boolean PendingEscapeKeyEvent(void)
1842 // check if any key press event is pending
1843 if (SDL_PeepEvents(&event, 1, SDL_PEEKEVENT, SDL_KEYDOWN, SDL_KEYDOWN) != 1)
1846 // check if pressed key is "Escape" key
1847 if (event.key.keysym.sym == KSYM_Escape)
1855 // ============================================================================
1856 // joystick functions
1857 // ============================================================================
1859 void InitJoysticks(void)
1863 #if defined(NO_JOYSTICK)
1864 return; // joysticks generally deactivated by compile-time directive
1867 // always start with reliable default values
1868 joystick.status = JOYSTICK_NOT_AVAILABLE;
1869 for (i = 0; i < MAX_PLAYERS; i++)
1870 joystick.nr[i] = -1; // no joystick configured
1875 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1877 return SDLReadJoystick(nr, x, y, b1, b2);
1880 boolean CheckJoystickOpened(int nr)
1882 return SDLCheckJoystickOpened(nr);
1885 void ClearJoystickState(void)
1887 SDLClearJoystickState();
1891 // ============================================================================
1892 // Emscripten functions
1893 // ============================================================================
1895 void InitEmscriptenFilesystem(void)
1897 #if defined(PLATFORM_EMSCRIPTEN)
1900 dir = UTF8ToString($0);
1902 Module.sync_done = 0;
1904 FS.mkdir(dir); // create persistent data directory
1905 FS.mount(IDBFS, {}, dir); // mount with IDBFS filesystem type
1906 FS.syncfs(true, function(err) // sync persistent data into memory
1909 Module.sync_done = 1;
1911 }, PERSISTENT_DIRECTORY);
1913 // wait for persistent data to be synchronized to memory
1914 while (emscripten_run_script_int("Module.sync_done") == 0)
1919 void SyncEmscriptenFilesystem(void)
1921 #if defined(PLATFORM_EMSCRIPTEN)
1924 FS.syncfs(function(err)