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 *command_filename,
73 char *config_filename, char *userdata_subdir,
74 char *program_basename, char *program_title,
75 char *icon_filename, char *cookie_prefix,
76 char *program_version_string, int program_version)
78 program.command_basepath = getBasePath(command_filename);
79 program.command_basename = getBaseName(command_filename);
81 program.config_filename = config_filename;
83 program.userdata_subdir = userdata_subdir;
84 program.userdata_path = getMainUserGameDataDir();
86 program.program_basename = program_basename;
87 program.program_title = program_title;
88 program.window_title = "(undefined)";
90 program.icon_filename = icon_filename;
92 program.cookie_prefix = cookie_prefix;
94 program.version_super = VERSION_SUPER(program_version);
95 program.version_major = VERSION_MAJOR(program_version);
96 program.version_minor = VERSION_MINOR(program_version);
97 program.version_patch = VERSION_PATCH(program_version);
98 program.version_ident = program_version;
100 program.version_string = program_version_string;
102 program.log_filename = getLogFilename(getLogBasename(program_basename));
103 program.log_file = program.log_file_default = stdout;
105 program.api_thread_count = 0;
107 program.headless = FALSE;
110 void InitNetworkInfo(boolean enabled, boolean connected, boolean serveronly,
111 char *server_host, int server_port)
113 network.enabled = enabled;
114 network.connected = connected;
115 network.serveronly = serveronly;
117 network.server_host = server_host;
118 network.server_port = server_port;
120 network.server_thread = NULL;
121 network.is_server_thread = FALSE;
124 void InitRuntimeInfo()
126 #if defined(HAS_TOUCH_DEVICE)
127 runtime.uses_touch_device = TRUE;
129 runtime.uses_touch_device = FALSE;
132 runtime.use_api_server = setup.use_api_server;
135 void SetWindowTitle(void)
137 program.window_title = program.window_title_function();
142 void InitWindowTitleFunction(char *(*window_title_function)(void))
144 program.window_title_function = window_title_function;
147 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
149 program.exit_message_function = exit_message_function;
152 void InitExitFunction(void (*exit_function)(int))
154 program.exit_function = exit_function;
156 // set signal handlers to custom exit function
157 // signal(SIGINT, exit_function);
158 signal(SIGTERM, exit_function);
160 // set exit function to automatically cleanup SDL stuff after exit()
164 void InitPlatformDependentStuff(void)
166 InitEmscriptenFilesystem();
168 // this is initialized in GetOptions(), but may already be used before
169 options.verbose = TRUE;
173 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
175 if (SDL_Init(sdl_init_flags) < 0)
176 Fail("SDL_Init() failed: %s", SDL_GetError());
181 void ClosePlatformDependentStuff(void)
186 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
187 int real_sx, int real_sy,
188 int full_sxsize, int full_sysize,
189 Bitmap *field_save_buffer)
195 gfx.real_sx = real_sx;
196 gfx.real_sy = real_sy;
197 gfx.full_sxsize = full_sxsize;
198 gfx.full_sysize = full_sysize;
200 gfx.field_save_buffer = field_save_buffer;
202 SetDrawDeactivationMask(REDRAW_NONE); // do not deactivate drawing
203 SetDrawBackgroundMask(REDRAW_NONE); // deactivate masked drawing
206 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
208 gfx.game_tile_size = game_tile_size;
209 gfx.standard_tile_size = standard_tile_size;
212 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
220 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
228 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
236 void InitGfxWindowInfo(int win_xsize, int win_ysize)
238 if (win_xsize != gfx.win_xsize || win_ysize != gfx.win_ysize)
240 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize);
242 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize);
244 ReCreateBitmap(&gfx.fade_bitmap_backup, win_xsize, win_ysize);
245 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize);
246 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize);
247 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize);
249 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
252 gfx.win_xsize = win_xsize;
253 gfx.win_ysize = win_ysize;
255 gfx.background_bitmap_mask = REDRAW_NONE;
258 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
260 // currently only used by MSDOS code to alloc VRAM buffer, if available
261 // 2009-03-24: also (temporarily?) used for overlapping blit workaround
262 gfx.scrollbuffer_width = scrollbuffer_width;
263 gfx.scrollbuffer_height = scrollbuffer_height;
266 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
268 gfx.clipping_enabled = enabled;
271 gfx.clip_width = width;
272 gfx.clip_height = height;
275 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(boolean))
277 gfx.draw_busy_anim_function = draw_busy_anim_function;
280 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int))
282 gfx.draw_global_anim_function = draw_global_anim_function;
285 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
287 gfx.draw_global_border_function = draw_global_border_function;
290 void InitGfxDrawTileCursorFunction(void (*draw_tile_cursor_function)(int, int))
292 gfx.draw_tile_cursor_function = draw_tile_cursor_function;
295 void InitGfxDrawEnvelopeRequestFunction(void (*draw_envelope_request_function)(int))
297 gfx.draw_envelope_request_function = draw_envelope_request_function;
300 void InitGfxCustomArtworkInfo(void)
302 gfx.override_level_graphics = FALSE;
303 gfx.override_level_sounds = FALSE;
304 gfx.override_level_music = FALSE;
306 gfx.draw_init_text = TRUE;
309 void InitGfxOtherSettings(void)
311 gfx.cursor_mode = CURSOR_DEFAULT;
312 gfx.cursor_mode_override = CURSOR_UNDEFINED;
313 gfx.cursor_mode_final = gfx.cursor_mode;
315 // prevent initially displaying custom mouse cursor in upper left corner
316 gfx.mouse_x = POS_OFFSCREEN;
317 gfx.mouse_y = POS_OFFSCREEN;
320 void InitTileCursorInfo(void)
322 tile_cursor.enabled = FALSE;
323 tile_cursor.active = FALSE;
324 tile_cursor.moving = FALSE;
326 tile_cursor.xpos = 0;
327 tile_cursor.ypos = 0;
330 tile_cursor.target_x = 0;
331 tile_cursor.target_y = 0;
336 tile_cursor.xsn_debug = FALSE;
339 void InitOverlayInfo(void)
341 overlay.enabled = FALSE;
342 overlay.active = FALSE;
344 overlay.show_grid = FALSE;
346 overlay.grid_button_highlight = CHAR_GRID_BUTTON_NONE;
347 overlay.grid_button_action = JOY_NO_ACTION;
349 SetOverlayGridSizeAndButtons();
351 #if defined(USE_TOUCH_INPUT_OVERLAY)
352 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
353 overlay.enabled = TRUE;
357 void SetOverlayGridSizeAndButtons(void)
359 int nr = GRID_ACTIVE_NR();
362 overlay.grid_xsize = setup.touch.grid_xsize[nr];
363 overlay.grid_ysize = setup.touch.grid_ysize[nr];
365 for (x = 0; x < MAX_GRID_XSIZE; x++)
366 for (y = 0; y < MAX_GRID_YSIZE; y++)
367 overlay.grid_button[x][y] = setup.touch.grid_button[nr][x][y];
370 void SetTileCursorEnabled(boolean enabled)
372 tile_cursor.enabled = enabled;
375 void SetTileCursorActive(boolean active)
377 tile_cursor.active = active;
380 void SetTileCursorTargetXY(int x, int y)
382 // delayed placement of tile selection cursor at target position
383 // (tile cursor will be moved to target position step by step)
385 tile_cursor.xpos = x;
386 tile_cursor.ypos = y;
387 tile_cursor.target_x = tile_cursor.sx + x * gfx.game_tile_size;
388 tile_cursor.target_y = tile_cursor.sy + y * gfx.game_tile_size;
390 tile_cursor.moving = TRUE;
393 void SetTileCursorXY(int x, int y)
395 // immediate placement of tile selection cursor at target position
397 SetTileCursorTargetXY(x, y);
399 tile_cursor.x = tile_cursor.target_x;
400 tile_cursor.y = tile_cursor.target_y;
402 tile_cursor.moving = FALSE;
405 void SetTileCursorSXSY(int sx, int sy)
411 void SetOverlayEnabled(boolean enabled)
413 overlay.enabled = enabled;
416 void SetOverlayActive(boolean active)
418 overlay.active = active;
421 void SetOverlayShowGrid(boolean show_grid)
423 overlay.show_grid = show_grid;
425 SetOverlayActive(show_grid);
428 SetOverlayEnabled(TRUE);
431 boolean GetOverlayEnabled(void)
433 return overlay.enabled;
436 boolean GetOverlayActive(void)
438 return overlay.active;
441 void SetDrawDeactivationMask(int draw_deactivation_mask)
443 gfx.draw_deactivation_mask = draw_deactivation_mask;
446 int GetDrawDeactivationMask(void)
448 return gfx.draw_deactivation_mask;
451 void SetDrawBackgroundMask(int draw_background_mask)
453 gfx.draw_background_mask = draw_background_mask;
456 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask,
457 int x, int y, int width, int height)
459 if (background_bitmap_tile != NULL)
460 gfx.background_bitmap_mask |= mask;
462 gfx.background_bitmap_mask &= ~mask;
464 if (background_bitmap_tile == NULL) // empty background requested
467 if (mask == REDRAW_ALL)
468 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap,
470 0, 0, video.width, video.height);
471 else if (mask == REDRAW_FIELD)
472 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap,
474 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
475 else if (mask == REDRAW_DOOR_1)
476 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap,
478 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
482 // ============================================================================
484 // ============================================================================
486 static int GetRealDepth(int depth)
488 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
491 static void sysFillRectangle(Bitmap *bitmap, int x, int y,
492 int width, int height, Pixel color)
494 SDLFillRectangle(bitmap, x, y, width, height, color);
496 if (bitmap == backbuffer)
497 SetRedrawMaskFromArea(x, y, width, height);
500 static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
501 int src_x, int src_y, int width, int height,
502 int dst_x, int dst_y, int mask_mode)
504 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
505 dst_x, dst_y, mask_mode);
507 if (dst_bitmap == backbuffer)
508 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
511 void LimitScreenUpdates(boolean enable)
513 SDLLimitScreenUpdates(enable);
516 void InitVideoDefaults(void)
518 video.default_depth = 32;
521 void InitVideoDisplay(void)
523 if (program.headless)
526 SDLInitVideoDisplay();
530 void CloseVideoDisplay(void)
532 KeyboardAutoRepeatOn();
534 SDL_QuitSubSystem(SDL_INIT_VIDEO);
537 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
540 video.height = height;
541 video.depth = GetRealDepth(depth);
543 video.screen_width = width;
544 video.screen_height = height;
545 video.screen_xoffset = 0;
546 video.screen_yoffset = 0;
548 video.fullscreen_available = FULLSCREEN_STATUS;
549 video.fullscreen_enabled = FALSE;
551 video.window_scaling_available = WINDOW_SCALING_STATUS;
553 video.frame_counter = 0;
554 video.frame_delay.count = 0;
555 video.frame_delay.value = GAME_FRAME_DELAY;
557 video.shifted_up = FALSE;
558 video.shifted_up_pos = 0;
559 video.shifted_up_pos_last = 0;
560 video.shifted_up_delay.count = 0;
561 video.shifted_up_delay.value = ONE_SECOND_DELAY / 4;
563 SDLInitVideoBuffer(fullscreen);
565 video.initialized = !program.headless;
570 static void FreeBitmapPointers(Bitmap *bitmap)
575 SDLFreeBitmapPointers(bitmap);
577 checked_free(bitmap->source_filename);
578 bitmap->source_filename = NULL;
581 static void TransferBitmapPointers(Bitmap *src_bitmap,
584 if (src_bitmap == NULL || dst_bitmap == NULL)
587 FreeBitmapPointers(dst_bitmap);
589 *dst_bitmap = *src_bitmap;
592 void FreeBitmap(Bitmap *bitmap)
597 FreeBitmapPointers(bitmap);
602 Bitmap *CreateBitmapStruct(void)
604 Bitmap *new_bitmap = checked_calloc(sizeof(Bitmap));
606 new_bitmap->alpha[0][0] = -1;
607 new_bitmap->alpha[0][1] = -1;
608 new_bitmap->alpha[1][0] = -1;
609 new_bitmap->alpha[1][1] = -1;
610 new_bitmap->alpha_next_blit = -1;
615 Bitmap *CreateBitmap(int width, int height, int depth)
617 Bitmap *new_bitmap = CreateBitmapStruct();
618 int real_width = MAX(1, width); // prevent zero bitmap width
619 int real_height = MAX(1, height); // prevent zero bitmap height
620 int real_depth = GetRealDepth(depth);
622 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
624 new_bitmap->width = real_width;
625 new_bitmap->height = real_height;
630 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
634 // if new bitmap size fits into old one, no need to re-create it
635 if (width <= (*bitmap)->width &&
636 height <= (*bitmap)->height)
639 // else adjust size so that old and new bitmap size fit into it
640 width = MAX(width, (*bitmap)->width);
641 height = MAX(height, (*bitmap)->height);
644 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
648 *bitmap = new_bitmap;
652 TransferBitmapPointers(new_bitmap, *bitmap);
658 static void CloseWindow(DrawWindow *window)
663 void SetRedrawMaskFromArea(int x, int y, int width, int height)
667 int x2 = x + width - 1;
668 int y2 = y + height - 1;
670 if (width == 0 || height == 0)
673 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
674 redraw_mask |= REDRAW_FIELD;
675 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
676 redraw_mask |= REDRAW_DOOR_1;
677 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
678 redraw_mask |= REDRAW_DOOR_2;
679 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
680 redraw_mask |= REDRAW_DOOR_3;
682 redraw_mask = REDRAW_ALL;
685 static boolean CheckDrawingArea(int x, int y, int draw_mask)
687 if (draw_mask == REDRAW_NONE)
690 if (draw_mask & REDRAW_ALL)
693 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
696 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
699 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
702 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
708 boolean DrawingDeactivatedField(void)
710 if (program.headless)
713 if (gfx.draw_deactivation_mask & REDRAW_FIELD)
719 boolean DrawingDeactivated(int x, int y)
721 return CheckDrawingArea(x, y, gfx.draw_deactivation_mask);
724 boolean DrawingOnBackground(int x, int y)
726 return (CheckDrawingArea(x, y, gfx.background_bitmap_mask) &&
727 CheckDrawingArea(x, y, gfx.draw_background_mask));
730 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
731 int *width, int *height, boolean is_dest)
733 int clip_x, clip_y, clip_width, clip_height;
735 if (gfx.clipping_enabled && is_dest) // only clip destination bitmap
737 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
738 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
739 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
740 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
746 clip_width = bitmap->width;
747 clip_height = bitmap->height;
750 // skip if rectangle completely outside bitmap
752 if (*x + *width <= clip_x ||
753 *y + *height <= clip_y ||
754 *x >= clip_x + clip_width ||
755 *y >= clip_y + clip_height)
758 // clip if rectangle overlaps bitmap
762 *width -= clip_x - *x;
765 else if (*x + *width > clip_x + clip_width)
767 *width = clip_x + clip_width - *x;
772 *height -= clip_y - *y;
775 else if (*y + *height > clip_y + clip_height)
777 *height = clip_y + clip_height - *y;
783 void SetBitmapAlphaNextBlit(Bitmap *bitmap, int alpha)
785 // set alpha value for next blitting of bitmap
786 bitmap->alpha_next_blit = alpha;
789 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
790 int src_x, int src_y, int width, int height,
791 int dst_x, int dst_y)
793 int dst_x_unclipped = dst_x;
794 int dst_y_unclipped = dst_y;
796 if (program.headless)
799 if (src_bitmap == NULL || dst_bitmap == NULL)
802 if (DrawingDeactivated(dst_x, dst_y))
805 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
806 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
809 // source x/y might need adjustment if destination x/y was clipped top/left
810 src_x += dst_x - dst_x_unclipped;
811 src_y += dst_y - dst_y_unclipped;
813 // !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!!
814 // !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!!
815 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
816 but is already fixed in SVN and should therefore finally be fixed with
817 the next official SDL release, which is probably version 1.2.14.) */
818 // !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!!
820 if (src_bitmap == dst_bitmap)
822 // needed when blitting directly to same bitmap -- should not be needed with
823 // recent SDL libraries, but apparently does not work in 1.2.11 directly
825 static Bitmap *tmp_bitmap = NULL;
826 static int tmp_bitmap_xsize = 0;
827 static int tmp_bitmap_ysize = 0;
829 // start with largest static bitmaps for initial bitmap size ...
830 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
832 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
833 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
836 // ... and allow for later re-adjustments due to custom artwork bitmaps
837 if (src_bitmap->width > tmp_bitmap_xsize ||
838 src_bitmap->height > tmp_bitmap_ysize)
840 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
841 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
843 FreeBitmap(tmp_bitmap);
848 if (tmp_bitmap == NULL)
849 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
852 sysCopyArea(src_bitmap, tmp_bitmap,
853 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
854 sysCopyArea(tmp_bitmap, dst_bitmap,
855 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
860 sysCopyArea(src_bitmap, dst_bitmap,
861 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
864 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
865 int src_x, int src_y, int src_width, int src_height,
866 int dst_x, int dst_y, int dst_width, int dst_height)
868 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
869 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
870 int dst_xsize = dst_width;
871 int dst_ysize = dst_height;
872 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
873 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
876 for (y = 0; y < src_ysteps; y++)
878 for (x = 0; x < src_xsteps; x++)
880 int draw_x = dst_x + x * src_xsize;
881 int draw_y = dst_y + y * src_ysize;
882 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
883 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
885 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
891 void FadeRectangle(int x, int y, int width, int height,
892 int fade_mode, int fade_delay, int post_delay,
893 void (*draw_border_function)(void))
895 // (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined)
896 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
899 SDLFadeRectangle(x, y, width, height,
900 fade_mode, fade_delay, post_delay, draw_border_function);
903 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
906 if (program.headless)
909 if (DrawingDeactivated(x, y))
912 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
915 sysFillRectangle(bitmap, x, y, width, height, color);
918 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
920 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
923 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
924 int width, int height)
926 if (DrawingOnBackground(x, y))
927 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
929 ClearRectangle(bitmap, x, y, width, height);
932 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
933 int src_x, int src_y, int width, int height,
934 int dst_x, int dst_y)
936 if (DrawingDeactivated(dst_x, dst_y))
939 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
940 dst_x, dst_y, BLIT_MASKED);
943 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
944 int src_x, int src_y, int width, int height,
945 int dst_x, int dst_y)
947 if (DrawingOnBackground(dst_x, dst_y))
950 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
954 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
958 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
962 void BlitTexture(Bitmap *bitmap,
963 int src_x, int src_y, int width, int height,
964 int dst_x, int dst_y)
969 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
973 void BlitTextureMasked(Bitmap *bitmap,
974 int src_x, int src_y, int width, int height,
975 int dst_x, int dst_y)
980 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
984 void BlitToScreen(Bitmap *bitmap,
985 int src_x, int src_y, int width, int height,
986 int dst_x, int dst_y)
991 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
992 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
993 width, height, dst_x, dst_y);
995 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
998 void BlitToScreenMasked(Bitmap *bitmap,
999 int src_x, int src_y, int width, int height,
1000 int dst_x, int dst_y)
1005 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
1006 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
1007 width, height, dst_x, dst_y);
1009 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
1012 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
1015 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
1018 static void DrawLine(Bitmap *bitmap, int from_x, int from_y,
1019 int to_x, int to_y, Pixel pixel, int line_width)
1023 if (program.headless)
1026 for (x = 0; x < line_width; x++)
1028 for (y = 0; y < line_width; y++)
1030 int dx = x - line_width / 2;
1031 int dy = y - line_width / 2;
1033 if ((x == 0 && y == 0) ||
1034 (x == 0 && y == line_width - 1) ||
1035 (x == line_width - 1 && y == 0) ||
1036 (x == line_width - 1 && y == line_width - 1))
1040 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
1045 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
1050 for (i = 0; i < num_points - 1; i++)
1051 DrawLine(bitmap, points[i].x, points[i].y,
1052 points[i + 1].x, points[i + 1].y, pixel, line_width);
1055 SDLDrawLines(bitmap->surface, points, num_points, pixel);
1059 Pixel GetPixel(Bitmap *bitmap, int x, int y)
1061 if (program.headless)
1064 if (x < 0 || x >= bitmap->width ||
1065 y < 0 || y >= bitmap->height)
1068 return SDLGetPixel(bitmap, x, y);
1071 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
1072 unsigned int color_g, unsigned int color_b)
1074 if (program.headless)
1077 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
1080 void KeyboardAutoRepeatOn(void)
1082 keyrepeat_status = TRUE;
1085 void KeyboardAutoRepeatOff(void)
1087 keyrepeat_status = FALSE;
1090 boolean SetVideoMode(boolean fullscreen)
1092 return SDLSetVideoMode(fullscreen);
1095 void SetVideoFrameDelay(unsigned int frame_delay_value)
1097 video.frame_delay.value = frame_delay_value;
1100 unsigned int GetVideoFrameDelay(void)
1102 return video.frame_delay.value;
1105 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
1107 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
1108 (!fullscreen && video.fullscreen_enabled))
1109 fullscreen = SetVideoMode(fullscreen);
1114 Bitmap *LoadImage(char *filename)
1118 new_bitmap = SDLLoadImage(filename);
1121 new_bitmap->source_filename = getStringCopy(filename);
1126 Bitmap *LoadCustomImage(char *basename)
1128 char *filename = getCustomImageFilename(basename);
1131 if (filename == NULL)
1132 Fail("LoadCustomImage(): cannot find file '%s'", basename);
1134 if ((new_bitmap = LoadImage(filename)) == NULL)
1135 Fail("LoadImage('%s') failed", basename);
1140 void ReloadCustomImage(Bitmap *bitmap, char *basename)
1142 char *filename = getCustomImageFilename(basename);
1145 if (filename == NULL) // (should never happen)
1147 Warn("ReloadCustomImage(): cannot find file '%s'", basename);
1152 if (strEqual(filename, bitmap->source_filename))
1154 // The old and new image are the same (have the same filename and path).
1155 // This usually means that this image does not exist in this graphic set
1156 // and a fallback to the existing image is done.
1161 if ((new_bitmap = LoadImage(filename)) == NULL)
1163 Warn("LoadImage('%s') failed", basename);
1168 if (bitmap->width != new_bitmap->width ||
1169 bitmap->height != new_bitmap->height)
1171 Warn("ReloadCustomImage: new image '%s' has wrong dimensions",
1174 FreeBitmap(new_bitmap);
1179 TransferBitmapPointers(new_bitmap, bitmap);
1183 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1185 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
1188 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1190 if (bitmaps[IMG_BITMAP_CUSTOM])
1192 // check if original sized bitmap points to custom sized bitmap
1193 if (bitmaps[IMG_BITMAP_PTR_ORIGINAL] == bitmaps[IMG_BITMAP_CUSTOM])
1195 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_PTR_ORIGINAL]);
1197 // keep pointer of previous custom size bitmap
1198 bitmaps[IMG_BITMAP_OTHER] = bitmaps[IMG_BITMAP_CUSTOM];
1200 // set original bitmap pointer to scaled original bitmap of other size
1201 bitmaps[IMG_BITMAP_PTR_ORIGINAL] = bitmaps[IMG_BITMAP_OTHER];
1203 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_PTR_ORIGINAL]);
1207 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1210 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1213 if (gfx.game_tile_size == gfx.standard_tile_size)
1215 // set game bitmap pointer to standard sized bitmap (already existing)
1216 bitmaps[IMG_BITMAP_PTR_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1221 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1222 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1223 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1225 bitmaps[IMG_BITMAP_CUSTOM] = ZoomBitmap(bitmap, width, height);
1227 // set game bitmap pointer to custom sized bitmap (newly created)
1228 bitmaps[IMG_BITMAP_PTR_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1231 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1232 int tile_size, boolean create_small_bitmaps)
1234 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1235 Bitmap *tmp_bitmap_final = NULL;
1236 Bitmap *tmp_bitmap_0 = NULL;
1237 Bitmap *tmp_bitmap_1 = NULL;
1238 Bitmap *tmp_bitmap_2 = NULL;
1239 Bitmap *tmp_bitmap_4 = NULL;
1240 Bitmap *tmp_bitmap_8 = NULL;
1241 Bitmap *tmp_bitmap_16 = NULL;
1242 Bitmap *tmp_bitmap_32 = NULL;
1243 int width_final, height_final;
1244 int width_0, height_0;
1245 int width_1, height_1;
1246 int width_2, height_2;
1247 int width_4, height_4;
1248 int width_8, height_8;
1249 int width_16, height_16;
1250 int width_32, height_32;
1251 int old_width, old_height;
1254 print_timestamp_init("CreateScaledBitmaps");
1256 old_width = old_bitmap->width;
1257 old_height = old_bitmap->height;
1259 // calculate new image dimensions for final image size
1260 width_final = old_width * zoom_factor;
1261 height_final = old_height * zoom_factor;
1263 // get image with final size (this might require scaling up)
1264 // ("final" size may result in non-standard tile size image)
1265 if (zoom_factor != 1)
1266 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1268 tmp_bitmap_final = old_bitmap;
1270 UPDATE_BUSY_STATE();
1272 width_0 = width_1 = width_final;
1273 height_0 = height_1 = height_final;
1275 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1277 if (create_small_bitmaps)
1279 // check if we have a non-gameplay tile size image
1280 if (tile_size != gfx.game_tile_size)
1282 // get image with gameplay tile size
1283 width_0 = width_final * gfx.game_tile_size / tile_size;
1284 height_0 = height_final * gfx.game_tile_size / tile_size;
1286 if (width_0 == old_width)
1287 tmp_bitmap_0 = old_bitmap;
1288 else if (width_0 == width_final)
1289 tmp_bitmap_0 = tmp_bitmap_final;
1291 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1293 UPDATE_BUSY_STATE();
1296 // check if we have a non-standard tile size image
1297 if (tile_size != gfx.standard_tile_size)
1299 // get image with standard tile size
1300 width_1 = width_final * gfx.standard_tile_size / tile_size;
1301 height_1 = height_final * gfx.standard_tile_size / tile_size;
1303 if (width_1 == old_width)
1304 tmp_bitmap_1 = old_bitmap;
1305 else if (width_1 == width_final)
1306 tmp_bitmap_1 = tmp_bitmap_final;
1307 else if (width_1 == width_0)
1308 tmp_bitmap_1 = tmp_bitmap_0;
1310 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1312 UPDATE_BUSY_STATE();
1315 // calculate new image dimensions for small images
1316 width_2 = width_1 / 2;
1317 height_2 = height_1 / 2;
1318 width_4 = width_1 / 4;
1319 height_4 = height_1 / 4;
1320 width_8 = width_1 / 8;
1321 height_8 = height_1 / 8;
1322 width_16 = width_1 / 16;
1323 height_16 = height_1 / 16;
1324 width_32 = width_1 / 32;
1325 height_32 = height_1 / 32;
1327 // get image with 1/2 of normal size (for use in the level editor)
1328 if (width_2 == old_width)
1329 tmp_bitmap_2 = old_bitmap;
1331 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1333 UPDATE_BUSY_STATE();
1335 // get image with 1/4 of normal size (for use in the level editor)
1336 if (width_4 == old_width)
1337 tmp_bitmap_4 = old_bitmap;
1339 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1341 UPDATE_BUSY_STATE();
1343 // get image with 1/8 of normal size (for use on the preview screen)
1344 if (width_8 == old_width)
1345 tmp_bitmap_8 = old_bitmap;
1347 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1349 UPDATE_BUSY_STATE();
1351 // get image with 1/16 of normal size (for use on the preview screen)
1352 if (width_16 == old_width)
1353 tmp_bitmap_16 = old_bitmap;
1355 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1357 UPDATE_BUSY_STATE();
1359 // get image with 1/32 of normal size (for use on the preview screen)
1360 if (width_32 == old_width)
1361 tmp_bitmap_32 = old_bitmap;
1363 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1365 UPDATE_BUSY_STATE();
1367 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1368 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1369 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1370 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1371 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1372 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1374 if (width_0 != width_1)
1375 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1377 if (bitmaps[IMG_BITMAP_CUSTOM])
1378 bitmaps[IMG_BITMAP_PTR_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1380 bitmaps[IMG_BITMAP_PTR_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1382 // store the "final" (up-scaled) original bitmap, if not already stored
1384 int tmp_bitmap_final_nr = -1;
1386 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1387 if (bitmaps[i] == tmp_bitmap_final)
1388 tmp_bitmap_final_nr = i;
1390 if (tmp_bitmap_final_nr == -1) // scaled original bitmap not stored
1392 // store pointer of scaled original bitmap (not used for any other size)
1393 bitmaps[IMG_BITMAP_OTHER] = tmp_bitmap_final;
1395 // set original bitmap pointer to scaled original bitmap of other size
1396 bitmaps[IMG_BITMAP_PTR_ORIGINAL] = bitmaps[IMG_BITMAP_OTHER];
1400 // set original bitmap pointer to corresponding sized bitmap
1401 bitmaps[IMG_BITMAP_PTR_ORIGINAL] = bitmaps[tmp_bitmap_final_nr];
1404 // free the "old" (unscaled) original bitmap, if not already stored
1406 boolean free_old_bitmap = TRUE;
1408 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1409 if (bitmaps[i] == old_bitmap)
1410 free_old_bitmap = FALSE;
1412 if (free_old_bitmap)
1414 // copy image filename from old to new standard sized bitmap
1415 bitmaps[IMG_BITMAP_STANDARD]->source_filename =
1416 getStringCopy(old_bitmap->source_filename);
1418 FreeBitmap(old_bitmap);
1423 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1425 // set original bitmap pointer to corresponding sized bitmap
1426 bitmaps[IMG_BITMAP_PTR_ORIGINAL] = bitmaps[IMG_BITMAP_32x32];
1428 if (old_bitmap != tmp_bitmap_1)
1429 FreeBitmap(old_bitmap);
1432 UPDATE_BUSY_STATE();
1434 print_timestamp_done("CreateScaledBitmaps");
1437 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1440 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1443 void CreateBitmapTextures(Bitmap **bitmaps)
1445 if (bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL)
1446 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_PTR_ORIGINAL]);
1448 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1451 void FreeBitmapTextures(Bitmap **bitmaps)
1453 if (bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL)
1454 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_PTR_ORIGINAL]);
1456 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1459 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1461 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1465 // ----------------------------------------------------------------------------
1466 // mouse pointer functions
1467 // ----------------------------------------------------------------------------
1469 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1471 // XPM image definitions
1472 static const char *cursor_image_none[] =
1474 // width height num_colors chars_per_pixel
1504 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1505 static const char *cursor_image_dot[] =
1507 // width height num_colors chars_per_pixel
1536 static const char **cursor_image_playfield = cursor_image_dot;
1538 // some people complained about a "white dot" on the screen and thought it
1539 // was a graphical error... OK, let's just remove the whole pointer :-)
1540 static const char **cursor_image_playfield = cursor_image_none;
1543 static const int cursor_bit_order = BIT_ORDER_MSB;
1545 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1547 struct MouseCursorInfo *cursor;
1548 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1549 int header_lines = 4;
1552 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1554 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1557 for (y = 0; y < cursor->width; y++)
1559 for (x = 0; x < cursor->height; x++)
1562 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1567 cursor->data[i] = cursor->mask[i] = 0;
1570 switch (image[header_lines + y][x])
1573 cursor->data[i] |= bit_mask;
1574 cursor->mask[i] |= bit_mask;
1578 cursor->mask[i] |= bit_mask;
1587 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1592 void SetMouseCursor(int mode)
1594 static struct MouseCursorInfo *cursor_none = NULL;
1595 static struct MouseCursorInfo *cursor_playfield = NULL;
1596 struct MouseCursorInfo *cursor_new;
1597 int mode_final = mode;
1599 if (cursor_none == NULL)
1600 cursor_none = get_cursor_from_image(cursor_image_none);
1602 if (cursor_playfield == NULL)
1603 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1605 if (gfx.cursor_mode_override != CURSOR_UNDEFINED)
1606 mode_final = gfx.cursor_mode_override;
1608 cursor_new = (mode_final == CURSOR_DEFAULT ? NULL :
1609 mode_final == CURSOR_NONE ? cursor_none :
1610 mode_final == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1612 SDLSetMouseCursor(cursor_new);
1614 gfx.cursor_mode = mode;
1615 gfx.cursor_mode_final = mode_final;
1618 void UpdateRawMousePosition(int mouse_x, int mouse_y)
1620 // mouse events do not contain logical screen size corrections yet
1621 SDLCorrectRawMousePosition(&mouse_x, &mouse_y);
1623 mouse_x -= video.screen_xoffset;
1624 mouse_y -= video.screen_yoffset;
1626 gfx.mouse_x = mouse_x;
1627 gfx.mouse_y = mouse_y;
1630 void UpdateMousePosition(void)
1632 int mouse_x, mouse_y;
1635 SDL_GetMouseState(&mouse_x, &mouse_y);
1637 UpdateRawMousePosition(mouse_x, mouse_y);
1641 // ============================================================================
1643 // ============================================================================
1645 void OpenAudio(void)
1647 // always start with reliable default values
1648 audio.sound_available = FALSE;
1649 audio.music_available = FALSE;
1650 audio.loops_available = FALSE;
1652 audio.sound_enabled = FALSE;
1653 audio.sound_deactivated = FALSE;
1655 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1656 audio.mixer_pid = 0;
1657 audio.device_name = NULL;
1658 audio.device_fd = -1;
1660 audio.num_channels = 0;
1661 audio.music_channel = 0;
1662 audio.first_sound_channel = 0;
1667 void CloseAudio(void)
1671 audio.sound_enabled = FALSE;
1674 void SetAudioMode(boolean enabled)
1676 if (!audio.sound_available)
1679 audio.sound_enabled = enabled;
1683 // ============================================================================
1685 // ============================================================================
1687 void InitEventFilter(EventFilter filter_function)
1689 SDL_SetEventFilter(filter_function, NULL);
1692 boolean PendingEvent(void)
1694 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1697 void WaitEvent(Event *event)
1699 SDLWaitEvent(event);
1702 void PeekEvent(Event *event)
1704 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1707 void PumpEvents(void)
1712 void CheckQuitEvent(void)
1714 if (SDL_QuitRequested())
1715 program.exit_function(0);
1718 Key GetEventKey(KeyEvent *event)
1720 // key up/down events in SDL2 do not return text characters anymore
1721 return event->keysym.sym;
1724 KeyMod HandleKeyModState(Key key, int key_status)
1726 static KeyMod current_modifiers = KMOD_None;
1728 if (key != KSYM_UNDEFINED) // new key => check for modifier key change
1730 KeyMod new_modifier = KMOD_None;
1735 new_modifier = KMOD_Shift_L;
1738 new_modifier = KMOD_Shift_R;
1740 case KSYM_Control_L:
1741 new_modifier = KMOD_Control_L;
1743 case KSYM_Control_R:
1744 new_modifier = KMOD_Control_R;
1747 new_modifier = KMOD_Meta_L;
1750 new_modifier = KMOD_Meta_R;
1753 new_modifier = KMOD_Alt_L;
1756 new_modifier = KMOD_Alt_R;
1762 if (key_status == KEY_PRESSED)
1763 current_modifiers |= new_modifier;
1765 current_modifiers &= ~new_modifier;
1768 return current_modifiers;
1771 KeyMod GetKeyModState(void)
1773 return (KeyMod)SDL_GetModState();
1776 KeyMod GetKeyModStateFromEvents(void)
1778 /* always use key modifier state as tracked from key events (this is needed
1779 if the modifier key event was injected into the event queue, but the key
1780 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1781 query the keys as held pressed on the keyboard) -- this case is currently
1782 only used to filter out clipboard insert events from "True X-Mouse" tool */
1784 return HandleKeyModState(KSYM_UNDEFINED, 0);
1787 void StartTextInput(int x, int y, int width, int height)
1789 textinput_status = TRUE;
1791 #if defined(HAS_SCREEN_KEYBOARD)
1792 SDL_StartTextInput();
1794 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1796 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1797 video.shifted_up_delay.count = SDL_GetTicks();
1798 video.shifted_up = TRUE;
1803 void StopTextInput(void)
1805 textinput_status = FALSE;
1807 #if defined(HAS_SCREEN_KEYBOARD)
1808 SDL_StopTextInput();
1810 if (video.shifted_up)
1812 video.shifted_up_pos = 0;
1813 video.shifted_up_delay.count = SDL_GetTicks();
1814 video.shifted_up = FALSE;
1819 void PushUserEvent(int code, int value1, int value2)
1823 SDL_memset(&event, 0, sizeof(event));
1825 event.type = EVENT_USER;
1827 event.value1 = value1;
1828 event.value2 = value2;
1830 SDL_PushEvent((SDL_Event *)&event);
1833 boolean PendingEscapeKeyEvent(void)
1839 // check if any key press event is pending
1840 if (SDL_PeepEvents(&event, 1, SDL_PEEKEVENT, SDL_KEYDOWN, SDL_KEYDOWN) != 1)
1843 // check if pressed key is "Escape" key
1844 if (event.key.keysym.sym == KSYM_Escape)
1852 // ============================================================================
1853 // joystick functions
1854 // ============================================================================
1856 void InitJoysticks(void)
1860 #if defined(NO_JOYSTICK)
1861 return; // joysticks generally deactivated by compile-time directive
1864 // always start with reliable default values
1865 joystick.status = JOYSTICK_NOT_AVAILABLE;
1866 for (i = 0; i < MAX_PLAYERS; i++)
1867 joystick.nr[i] = -1; // no joystick configured
1872 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1874 return SDLReadJoystick(nr, x, y, b1, b2);
1877 boolean CheckJoystickOpened(int nr)
1879 return SDLCheckJoystickOpened(nr);
1882 void ClearJoystickState(void)
1884 SDLClearJoystickState();
1888 // ============================================================================
1889 // Emscripten functions
1890 // ============================================================================
1892 void InitEmscriptenFilesystem(void)
1894 #if defined(PLATFORM_EMSCRIPTEN)
1897 dir = UTF8ToString($0);
1899 Module.sync_done = 0;
1901 FS.mkdir(dir); // create persistent data directory
1902 FS.mount(IDBFS, {}, dir); // mount with IDBFS filesystem type
1903 FS.syncfs(true, function(err) // sync persistent data into memory
1906 Module.sync_done = 1;
1908 }, PERSISTENT_DIRECTORY);
1910 // wait for persistent data to be synchronized to memory
1911 while (emscripten_run_script_int("Module.sync_done") == 0)
1916 void SyncEmscriptenFilesystem(void)
1918 #if defined(PLATFORM_EMSCRIPTEN)
1921 FS.syncfs(function(err)