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;
44 LevelDirTree *leveldir_first_all = NULL;
45 LevelDirTree *leveldir_first = NULL;
46 LevelDirTree *leveldir_current = NULL;
49 struct LevelSetInfo levelset;
50 struct LevelStats level_stats[MAX_LEVELS];
52 DrawWindow *window = NULL;
53 DrawBuffer *backbuffer = NULL;
54 DrawBuffer *drawto = NULL;
56 int button_status = MB_NOT_PRESSED;
57 boolean motion_status = FALSE;
58 int wheel_steps = DEFAULT_WHEEL_STEPS;
59 boolean keyrepeat_status = TRUE;
60 boolean textinput_status = FALSE;
62 int redraw_mask = REDRAW_NONE;
67 // ============================================================================
68 // init/close functions
69 // ============================================================================
71 void InitProgramInfo(char *argv0, char *config_filename, char *userdata_subdir,
72 char *program_title, char *icon_title,
73 char *icon_filename, char *cookie_prefix,
74 char *program_version_string, int program_version)
76 program.command_basepath = getBasePath(argv0);
77 program.command_basename = getBaseName(argv0);
79 program.config_filename = config_filename;
81 program.userdata_subdir = userdata_subdir;
82 program.userdata_path = getUserGameDataDir();
84 program.program_title = program_title;
85 program.window_title = "(undefined)";
86 program.icon_title = icon_title;
88 program.icon_filename = icon_filename;
90 program.cookie_prefix = cookie_prefix;
92 program.version_super = VERSION_SUPER(program_version);
93 program.version_major = VERSION_MAJOR(program_version);
94 program.version_minor = VERSION_MINOR(program_version);
95 program.version_patch = VERSION_PATCH(program_version);
96 program.version_ident = program_version;
98 program.version_string = program_version_string;
100 program.log_filename[LOG_OUT_ID] = getLogFilename(LOG_OUT_BASENAME);
101 program.log_filename[LOG_ERR_ID] = getLogFilename(LOG_ERR_BASENAME);
102 program.log_file[LOG_OUT_ID] = program.log_file_default[LOG_OUT_ID] = stdout;
103 program.log_file[LOG_ERR_ID] = program.log_file_default[LOG_ERR_ID] = stderr;
105 program.headless = FALSE;
108 void InitNetworkInfo(boolean enabled, boolean connected, boolean serveronly,
109 char *server_host, int server_port)
111 network.enabled = enabled;
112 network.connected = connected;
113 network.serveronly = serveronly;
115 network.server_host = server_host;
116 network.server_port = server_port;
118 network.server_thread = NULL;
119 network.is_server_thread = FALSE;
122 void InitRuntimeInfo()
124 #if defined(HAS_TOUCH_DEVICE)
125 runtime.uses_touch_device = TRUE;
127 runtime.uses_touch_device = FALSE;
131 void InitScoresInfo(void)
133 char *global_scores_dir = getPath2(getCommonDataDir(), SCORES_DIRECTORY);
135 program.global_scores = directoryExists(global_scores_dir);
136 program.many_scores_per_name = !program.global_scores;
141 if (program.global_scores)
143 Debug("internal:path", "Using global, multi-user scores directory '%s'.",
145 Debug("internal:path", "Remove to enable single-user scores directory.");
146 Debug("internal:path", "(This enables multipe score entries per user.)");
150 Debug("internal:path", "Using private, single-user scores directory.");
155 free(global_scores_dir);
158 void SetWindowTitle(void)
160 program.window_title = program.window_title_function();
165 void InitWindowTitleFunction(char *(*window_title_function)(void))
167 program.window_title_function = window_title_function;
170 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
172 program.exit_message_function = exit_message_function;
175 void InitExitFunction(void (*exit_function)(int))
177 program.exit_function = exit_function;
179 // set signal handlers to custom exit function
180 // signal(SIGINT, exit_function);
181 signal(SIGTERM, exit_function);
183 // set exit function to automatically cleanup SDL stuff after exit()
187 void InitPlatformDependentStuff(void)
189 // this is initialized in GetOptions(), but may already be used before
190 options.verbose = TRUE;
194 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
196 if (SDL_Init(sdl_init_flags) < 0)
197 Fail("SDL_Init() failed: %s", SDL_GetError());
202 void ClosePlatformDependentStuff(void)
207 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
208 int real_sx, int real_sy,
209 int full_sxsize, int full_sysize,
210 Bitmap *field_save_buffer)
216 gfx.real_sx = real_sx;
217 gfx.real_sy = real_sy;
218 gfx.full_sxsize = full_sxsize;
219 gfx.full_sysize = full_sysize;
221 gfx.field_save_buffer = field_save_buffer;
223 SetDrawDeactivationMask(REDRAW_NONE); // do not deactivate drawing
224 SetDrawBackgroundMask(REDRAW_NONE); // deactivate masked drawing
227 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
229 gfx.game_tile_size = game_tile_size;
230 gfx.standard_tile_size = standard_tile_size;
233 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
241 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
249 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
257 void InitGfxWindowInfo(int win_xsize, int win_ysize)
259 if (win_xsize != gfx.win_xsize || win_ysize != gfx.win_ysize)
261 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize);
263 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize);
265 ReCreateBitmap(&gfx.fade_bitmap_backup, win_xsize, win_ysize);
266 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize);
267 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize);
268 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize);
270 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
273 gfx.win_xsize = win_xsize;
274 gfx.win_ysize = win_ysize;
276 gfx.background_bitmap_mask = REDRAW_NONE;
279 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
281 // currently only used by MSDOS code to alloc VRAM buffer, if available
282 // 2009-03-24: also (temporarily?) used for overlapping blit workaround
283 gfx.scrollbuffer_width = scrollbuffer_width;
284 gfx.scrollbuffer_height = scrollbuffer_height;
287 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
289 gfx.clipping_enabled = enabled;
292 gfx.clip_width = width;
293 gfx.clip_height = height;
296 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
298 gfx.draw_busy_anim_function = draw_busy_anim_function;
301 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int))
303 gfx.draw_global_anim_function = draw_global_anim_function;
306 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
308 gfx.draw_global_border_function = draw_global_border_function;
311 void InitGfxDrawTileCursorFunction(void (*draw_tile_cursor_function)(int))
313 gfx.draw_tile_cursor_function = draw_tile_cursor_function;
316 void InitGfxCustomArtworkInfo(void)
318 gfx.override_level_graphics = FALSE;
319 gfx.override_level_sounds = FALSE;
320 gfx.override_level_music = FALSE;
322 gfx.draw_init_text = TRUE;
325 void InitGfxOtherSettings(void)
327 gfx.cursor_mode = CURSOR_DEFAULT;
328 gfx.cursor_mode_override = CURSOR_UNDEFINED;
329 gfx.cursor_mode_final = gfx.cursor_mode;
331 // prevent initially displaying custom mouse cursor in upper left corner
332 gfx.mouse_x = POS_OFFSCREEN;
333 gfx.mouse_y = POS_OFFSCREEN;
336 void InitTileCursorInfo(void)
338 tile_cursor.enabled = FALSE;
339 tile_cursor.active = FALSE;
340 tile_cursor.moving = FALSE;
342 tile_cursor.xpos = 0;
343 tile_cursor.ypos = 0;
346 tile_cursor.target_x = 0;
347 tile_cursor.target_y = 0;
353 void InitOverlayInfo(void)
355 int nr = GRID_ACTIVE_NR();
358 overlay.enabled = FALSE;
359 overlay.active = FALSE;
361 overlay.show_grid = FALSE;
363 overlay.grid_xsize = setup.touch.grid_xsize[nr];
364 overlay.grid_ysize = setup.touch.grid_ysize[nr];
366 for (x = 0; x < MAX_GRID_XSIZE; x++)
367 for (y = 0; y < MAX_GRID_YSIZE; y++)
368 overlay.grid_button[x][y] = setup.touch.grid_button[nr][x][y];
370 overlay.grid_button_highlight = CHAR_GRID_BUTTON_NONE;
371 overlay.grid_button_action = JOY_NO_ACTION;
373 #if defined(USE_TOUCH_INPUT_OVERLAY)
374 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
375 overlay.enabled = TRUE;
379 void SetTileCursorEnabled(boolean enabled)
381 tile_cursor.enabled = enabled;
384 void SetTileCursorActive(boolean active)
386 tile_cursor.active = active;
389 void SetTileCursorTargetXY(int x, int y)
391 // delayed placement of tile selection cursor at target position
392 // (tile cursor will be moved to target position step by step)
394 tile_cursor.xpos = x;
395 tile_cursor.ypos = y;
396 tile_cursor.target_x = tile_cursor.sx + x * gfx.game_tile_size;
397 tile_cursor.target_y = tile_cursor.sy + y * gfx.game_tile_size;
399 tile_cursor.moving = TRUE;
402 void SetTileCursorXY(int x, int y)
404 // immediate placement of tile selection cursor at target position
406 SetTileCursorTargetXY(x, y);
408 tile_cursor.x = tile_cursor.target_x;
409 tile_cursor.y = tile_cursor.target_y;
411 tile_cursor.moving = FALSE;
414 void SetTileCursorSXSY(int sx, int sy)
420 void SetOverlayEnabled(boolean enabled)
422 overlay.enabled = enabled;
425 void SetOverlayActive(boolean active)
427 overlay.active = active;
430 void SetOverlayShowGrid(boolean show_grid)
432 overlay.show_grid = show_grid;
434 SetOverlayActive(show_grid);
437 SetOverlayEnabled(TRUE);
440 boolean GetOverlayEnabled(void)
442 return overlay.enabled;
445 boolean GetOverlayActive(void)
447 return overlay.active;
450 void SetDrawDeactivationMask(int draw_deactivation_mask)
452 gfx.draw_deactivation_mask = draw_deactivation_mask;
455 int GetDrawDeactivationMask(void)
457 return gfx.draw_deactivation_mask;
460 void SetDrawBackgroundMask(int draw_background_mask)
462 gfx.draw_background_mask = draw_background_mask;
465 static void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
467 if (background_bitmap_tile != NULL)
468 gfx.background_bitmap_mask |= mask;
470 gfx.background_bitmap_mask &= ~mask;
472 if (background_bitmap_tile == NULL) // empty background requested
475 if (mask == REDRAW_ALL)
476 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
477 0, 0, video.width, video.height);
478 else if (mask == REDRAW_FIELD)
479 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
480 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
481 else if (mask == REDRAW_DOOR_1)
482 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
483 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
486 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
488 // remove every mask before setting mask for window
489 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
490 SetBackgroundBitmap(NULL, 0xffff); // !!! FIX THIS !!!
491 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
494 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
496 // remove window area mask before setting mask for main area
497 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
498 SetBackgroundBitmap(NULL, REDRAW_ALL); // !!! FIX THIS !!!
499 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
502 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
504 // remove window area mask before setting mask for door area
505 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
506 SetBackgroundBitmap(NULL, REDRAW_ALL); // !!! FIX THIS !!!
507 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
511 // ============================================================================
513 // ============================================================================
515 static int GetRealDepth(int depth)
517 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
520 static void sysFillRectangle(Bitmap *bitmap, int x, int y,
521 int width, int height, Pixel color)
523 SDLFillRectangle(bitmap, x, y, width, height, color);
525 if (bitmap == backbuffer)
526 SetRedrawMaskFromArea(x, y, width, height);
529 static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
530 int src_x, int src_y, int width, int height,
531 int dst_x, int dst_y, int mask_mode)
533 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
534 dst_x, dst_y, mask_mode);
536 if (dst_bitmap == backbuffer)
537 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
540 void LimitScreenUpdates(boolean enable)
542 SDLLimitScreenUpdates(enable);
545 void InitVideoDefaults(void)
547 video.default_depth = 32;
550 void InitVideoDisplay(void)
552 if (program.headless)
555 SDLInitVideoDisplay();
559 void CloseVideoDisplay(void)
561 KeyboardAutoRepeatOn();
563 SDL_QuitSubSystem(SDL_INIT_VIDEO);
566 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
569 video.height = height;
570 video.depth = GetRealDepth(depth);
572 video.screen_width = width;
573 video.screen_height = height;
574 video.screen_xoffset = 0;
575 video.screen_yoffset = 0;
577 video.fullscreen_available = FULLSCREEN_STATUS;
578 video.fullscreen_enabled = FALSE;
580 video.window_scaling_available = WINDOW_SCALING_STATUS;
582 video.frame_counter = 0;
583 video.frame_delay = 0;
584 video.frame_delay_value = GAME_FRAME_DELAY;
586 video.shifted_up = FALSE;
587 video.shifted_up_pos = 0;
588 video.shifted_up_pos_last = 0;
589 video.shifted_up_delay = 0;
590 video.shifted_up_delay_value = ONE_SECOND_DELAY / 4;
592 SDLInitVideoBuffer(fullscreen);
594 video.initialized = !program.headless;
599 static void FreeBitmapPointers(Bitmap *bitmap)
604 SDLFreeBitmapPointers(bitmap);
606 checked_free(bitmap->source_filename);
607 bitmap->source_filename = NULL;
610 static void TransferBitmapPointers(Bitmap *src_bitmap,
613 if (src_bitmap == NULL || dst_bitmap == NULL)
616 FreeBitmapPointers(dst_bitmap);
618 *dst_bitmap = *src_bitmap;
621 void FreeBitmap(Bitmap *bitmap)
626 FreeBitmapPointers(bitmap);
631 Bitmap *CreateBitmapStruct(void)
633 return checked_calloc(sizeof(Bitmap));
636 Bitmap *CreateBitmap(int width, int height, int depth)
638 Bitmap *new_bitmap = CreateBitmapStruct();
639 int real_width = MAX(1, width); // prevent zero bitmap width
640 int real_height = MAX(1, height); // prevent zero bitmap height
641 int real_depth = GetRealDepth(depth);
643 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
645 new_bitmap->width = real_width;
646 new_bitmap->height = real_height;
651 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
655 // if new bitmap size fits into old one, no need to re-create it
656 if (width <= (*bitmap)->width &&
657 height <= (*bitmap)->height)
660 // else adjust size so that old and new bitmap size fit into it
661 width = MAX(width, (*bitmap)->width);
662 height = MAX(height, (*bitmap)->height);
665 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
669 *bitmap = new_bitmap;
673 TransferBitmapPointers(new_bitmap, *bitmap);
679 static void CloseWindow(DrawWindow *window)
684 void SetRedrawMaskFromArea(int x, int y, int width, int height)
688 int x2 = x + width - 1;
689 int y2 = y + height - 1;
691 if (width == 0 || height == 0)
694 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
695 redraw_mask |= REDRAW_FIELD;
696 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
697 redraw_mask |= REDRAW_DOOR_1;
698 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
699 redraw_mask |= REDRAW_DOOR_2;
700 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
701 redraw_mask |= REDRAW_DOOR_3;
703 redraw_mask = REDRAW_ALL;
706 static boolean CheckDrawingArea(int x, int y, int width, int height,
709 if (draw_mask == REDRAW_NONE)
712 if (draw_mask & REDRAW_ALL)
715 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
718 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
721 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
724 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
730 boolean DrawingDeactivatedField(void)
732 if (program.headless)
735 if (gfx.draw_deactivation_mask & REDRAW_FIELD)
741 boolean DrawingDeactivated(int x, int y, int width, int height)
743 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
746 boolean DrawingOnBackground(int x, int y)
748 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
749 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
752 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
753 int *width, int *height, boolean is_dest)
755 int clip_x, clip_y, clip_width, clip_height;
757 if (gfx.clipping_enabled && is_dest) // only clip destination bitmap
759 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
760 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
761 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
762 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
768 clip_width = bitmap->width;
769 clip_height = bitmap->height;
772 // skip if rectangle completely outside bitmap
774 if (*x + *width <= clip_x ||
775 *y + *height <= clip_y ||
776 *x >= clip_x + clip_width ||
777 *y >= clip_y + clip_height)
780 // clip if rectangle overlaps bitmap
784 *width -= clip_x - *x;
787 else if (*x + *width > clip_x + clip_width)
789 *width = clip_x + clip_width - *x;
794 *height -= clip_y - *y;
797 else if (*y + *height > clip_y + clip_height)
799 *height = clip_y + clip_height - *y;
805 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
806 int src_x, int src_y, int width, int height,
807 int dst_x, int dst_y)
809 int dst_x_unclipped = dst_x;
810 int dst_y_unclipped = dst_y;
812 if (program.headless)
815 if (src_bitmap == NULL || dst_bitmap == NULL)
818 if (DrawingDeactivated(dst_x, dst_y, width, height))
821 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
822 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
825 // source x/y might need adjustment if destination x/y was clipped top/left
826 src_x += dst_x - dst_x_unclipped;
827 src_y += dst_y - dst_y_unclipped;
829 // !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!!
830 // !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!!
831 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
832 but is already fixed in SVN and should therefore finally be fixed with
833 the next official SDL release, which is probably version 1.2.14.) */
834 // !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!!
836 if (src_bitmap == dst_bitmap)
838 // needed when blitting directly to same bitmap -- should not be needed with
839 // recent SDL libraries, but apparently does not work in 1.2.11 directly
841 static Bitmap *tmp_bitmap = NULL;
842 static int tmp_bitmap_xsize = 0;
843 static int tmp_bitmap_ysize = 0;
845 // start with largest static bitmaps for initial bitmap size ...
846 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
848 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
849 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
852 // ... and allow for later re-adjustments due to custom artwork bitmaps
853 if (src_bitmap->width > tmp_bitmap_xsize ||
854 src_bitmap->height > tmp_bitmap_ysize)
856 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
857 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
859 FreeBitmap(tmp_bitmap);
864 if (tmp_bitmap == NULL)
865 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
868 sysCopyArea(src_bitmap, tmp_bitmap,
869 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
870 sysCopyArea(tmp_bitmap, dst_bitmap,
871 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
876 sysCopyArea(src_bitmap, dst_bitmap,
877 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
880 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
881 int src_x, int src_y, int src_width, int src_height,
882 int dst_x, int dst_y, int dst_width, int dst_height)
884 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
885 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
886 int dst_xsize = dst_width;
887 int dst_ysize = dst_height;
888 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
889 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
892 for (y = 0; y < src_ysteps; y++)
894 for (x = 0; x < src_xsteps; x++)
896 int draw_x = dst_x + x * src_xsize;
897 int draw_y = dst_y + y * src_ysize;
898 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
899 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
901 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
907 void FadeRectangle(int x, int y, int width, int height,
908 int fade_mode, int fade_delay, int post_delay,
909 void (*draw_border_function)(void))
911 // (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined)
912 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
915 SDLFadeRectangle(x, y, width, height,
916 fade_mode, fade_delay, post_delay, draw_border_function);
919 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
922 if (DrawingDeactivated(x, y, width, height))
925 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
928 sysFillRectangle(bitmap, x, y, width, height, color);
931 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
933 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
936 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
937 int width, int height)
939 if (DrawingOnBackground(x, y))
940 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
942 ClearRectangle(bitmap, x, y, width, height);
945 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
946 int src_x, int src_y, int width, int height,
947 int dst_x, int dst_y)
949 if (DrawingDeactivated(dst_x, dst_y, width, height))
952 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
953 dst_x, dst_y, BLIT_MASKED);
956 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
957 int src_x, int src_y, int width, int height,
958 int dst_x, int dst_y)
960 if (DrawingOnBackground(dst_x, dst_y))
963 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
967 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
971 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
975 void BlitTexture(Bitmap *bitmap,
976 int src_x, int src_y, int width, int height,
977 int dst_x, int dst_y)
982 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
986 void BlitTextureMasked(Bitmap *bitmap,
987 int src_x, int src_y, int width, int height,
988 int dst_x, int dst_y)
993 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
997 void BlitToScreen(Bitmap *bitmap,
998 int src_x, int src_y, int width, int height,
999 int dst_x, int dst_y)
1004 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
1005 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
1006 width, height, dst_x, dst_y);
1008 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
1011 void BlitToScreenMasked(Bitmap *bitmap,
1012 int src_x, int src_y, int width, int height,
1013 int dst_x, int dst_y)
1018 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
1019 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
1020 width, height, dst_x, dst_y);
1022 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
1025 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
1028 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
1031 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
1034 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
1037 static void DrawLine(Bitmap *bitmap, int from_x, int from_y,
1038 int to_x, int to_y, Pixel pixel, int line_width)
1042 if (program.headless)
1045 for (x = 0; x < line_width; x++)
1047 for (y = 0; y < line_width; y++)
1049 int dx = x - line_width / 2;
1050 int dy = y - line_width / 2;
1052 if ((x == 0 && y == 0) ||
1053 (x == 0 && y == line_width - 1) ||
1054 (x == line_width - 1 && y == 0) ||
1055 (x == line_width - 1 && y == line_width - 1))
1059 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
1064 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
1069 for (i = 0; i < num_points - 1; i++)
1070 DrawLine(bitmap, points[i].x, points[i].y,
1071 points[i + 1].x, points[i + 1].y, pixel, line_width);
1074 SDLDrawLines(bitmap->surface, points, num_points, pixel);
1078 Pixel GetPixel(Bitmap *bitmap, int x, int y)
1080 if (program.headless)
1083 if (x < 0 || x >= bitmap->width ||
1084 y < 0 || y >= bitmap->height)
1087 return SDLGetPixel(bitmap, x, y);
1090 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
1091 unsigned int color_g, unsigned int color_b)
1093 if (program.headless)
1096 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
1099 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
1101 unsigned int color_r = (color >> 16) & 0xff;
1102 unsigned int color_g = (color >> 8) & 0xff;
1103 unsigned int color_b = (color >> 0) & 0xff;
1105 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
1108 void KeyboardAutoRepeatOn(void)
1110 keyrepeat_status = TRUE;
1113 void KeyboardAutoRepeatOff(void)
1115 keyrepeat_status = FALSE;
1118 boolean SetVideoMode(boolean fullscreen)
1120 return SDLSetVideoMode(fullscreen);
1123 void SetVideoFrameDelay(unsigned int frame_delay_value)
1125 video.frame_delay_value = frame_delay_value;
1128 unsigned int GetVideoFrameDelay(void)
1130 return video.frame_delay_value;
1133 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
1135 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
1136 (!fullscreen && video.fullscreen_enabled))
1137 fullscreen = SetVideoMode(fullscreen);
1142 Bitmap *LoadImage(char *filename)
1146 new_bitmap = SDLLoadImage(filename);
1149 new_bitmap->source_filename = getStringCopy(filename);
1154 Bitmap *LoadCustomImage(char *basename)
1156 char *filename = getCustomImageFilename(basename);
1159 if (filename == NULL)
1160 Fail("LoadCustomImage(): cannot find file '%s'", basename);
1162 if ((new_bitmap = LoadImage(filename)) == NULL)
1163 Fail("LoadImage('%s') failed", basename);
1168 void ReloadCustomImage(Bitmap *bitmap, char *basename)
1170 char *filename = getCustomImageFilename(basename);
1173 if (filename == NULL) // (should never happen)
1175 Warn("ReloadCustomImage(): cannot find file '%s'", basename);
1180 if (strEqual(filename, bitmap->source_filename))
1182 // The old and new image are the same (have the same filename and path).
1183 // This usually means that this image does not exist in this graphic set
1184 // and a fallback to the existing image is done.
1189 if ((new_bitmap = LoadImage(filename)) == NULL)
1191 Warn("LoadImage('%s') failed", basename);
1196 if (bitmap->width != new_bitmap->width ||
1197 bitmap->height != new_bitmap->height)
1199 Warn("ReloadCustomImage: new image '%s' has wrong dimensions",
1202 FreeBitmap(new_bitmap);
1207 TransferBitmapPointers(new_bitmap, bitmap);
1211 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1213 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
1216 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1218 if (bitmaps[IMG_BITMAP_CUSTOM])
1220 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1222 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1225 if (gfx.game_tile_size == gfx.standard_tile_size)
1227 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1232 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1233 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1234 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1236 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1238 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1239 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1242 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1243 int tile_size, boolean create_small_bitmaps)
1245 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1246 Bitmap *tmp_bitmap_final = NULL;
1247 Bitmap *tmp_bitmap_0 = NULL;
1248 Bitmap *tmp_bitmap_1 = NULL;
1249 Bitmap *tmp_bitmap_2 = NULL;
1250 Bitmap *tmp_bitmap_4 = NULL;
1251 Bitmap *tmp_bitmap_8 = NULL;
1252 Bitmap *tmp_bitmap_16 = NULL;
1253 Bitmap *tmp_bitmap_32 = NULL;
1254 int width_final, height_final;
1255 int width_0, height_0;
1256 int width_1, height_1;
1257 int width_2, height_2;
1258 int width_4, height_4;
1259 int width_8, height_8;
1260 int width_16, height_16;
1261 int width_32, height_32;
1262 int old_width, old_height;
1265 print_timestamp_init("CreateScaledBitmaps");
1267 old_width = old_bitmap->width;
1268 old_height = old_bitmap->height;
1270 // calculate new image dimensions for final image size
1271 width_final = old_width * zoom_factor;
1272 height_final = old_height * zoom_factor;
1274 // get image with final size (this might require scaling up)
1275 // ("final" size may result in non-standard tile size image)
1276 if (zoom_factor != 1)
1277 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1279 tmp_bitmap_final = old_bitmap;
1281 UPDATE_BUSY_STATE();
1283 width_0 = width_1 = width_final;
1284 height_0 = height_1 = height_final;
1286 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1288 if (create_small_bitmaps)
1290 // check if we have a non-gameplay tile size image
1291 if (tile_size != gfx.game_tile_size)
1293 // get image with gameplay tile size
1294 width_0 = width_final * gfx.game_tile_size / tile_size;
1295 height_0 = height_final * gfx.game_tile_size / tile_size;
1297 if (width_0 == old_width)
1298 tmp_bitmap_0 = old_bitmap;
1299 else if (width_0 == width_final)
1300 tmp_bitmap_0 = tmp_bitmap_final;
1302 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1304 UPDATE_BUSY_STATE();
1307 // check if we have a non-standard tile size image
1308 if (tile_size != gfx.standard_tile_size)
1310 // get image with standard tile size
1311 width_1 = width_final * gfx.standard_tile_size / tile_size;
1312 height_1 = height_final * gfx.standard_tile_size / tile_size;
1314 if (width_1 == old_width)
1315 tmp_bitmap_1 = old_bitmap;
1316 else if (width_1 == width_final)
1317 tmp_bitmap_1 = tmp_bitmap_final;
1318 else if (width_1 == width_0)
1319 tmp_bitmap_1 = tmp_bitmap_0;
1321 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1323 UPDATE_BUSY_STATE();
1326 // calculate new image dimensions for small images
1327 width_2 = width_1 / 2;
1328 height_2 = height_1 / 2;
1329 width_4 = width_1 / 4;
1330 height_4 = height_1 / 4;
1331 width_8 = width_1 / 8;
1332 height_8 = height_1 / 8;
1333 width_16 = width_1 / 16;
1334 height_16 = height_1 / 16;
1335 width_32 = width_1 / 32;
1336 height_32 = height_1 / 32;
1338 // get image with 1/2 of normal size (for use in the level editor)
1339 if (width_2 == old_width)
1340 tmp_bitmap_2 = old_bitmap;
1342 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1344 UPDATE_BUSY_STATE();
1346 // get image with 1/4 of normal size (for use in the level editor)
1347 if (width_4 == old_width)
1348 tmp_bitmap_4 = old_bitmap;
1350 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1352 UPDATE_BUSY_STATE();
1354 // get image with 1/8 of normal size (for use on the preview screen)
1355 if (width_8 == old_width)
1356 tmp_bitmap_8 = old_bitmap;
1358 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1360 UPDATE_BUSY_STATE();
1362 // get image with 1/16 of normal size (for use on the preview screen)
1363 if (width_16 == old_width)
1364 tmp_bitmap_16 = old_bitmap;
1366 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1368 UPDATE_BUSY_STATE();
1370 // get image with 1/32 of normal size (for use on the preview screen)
1371 if (width_32 == old_width)
1372 tmp_bitmap_32 = old_bitmap;
1374 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1376 UPDATE_BUSY_STATE();
1378 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1379 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1380 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1381 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1382 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1383 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1385 if (width_0 != width_1)
1386 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1388 if (bitmaps[IMG_BITMAP_CUSTOM])
1389 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1391 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1393 boolean free_old_bitmap = TRUE;
1395 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1396 if (bitmaps[i] == old_bitmap)
1397 free_old_bitmap = FALSE;
1399 if (free_old_bitmap)
1401 // copy image filename from old to new standard sized bitmap
1402 bitmaps[IMG_BITMAP_STANDARD]->source_filename =
1403 getStringCopy(old_bitmap->source_filename);
1405 FreeBitmap(old_bitmap);
1410 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1413 UPDATE_BUSY_STATE();
1415 print_timestamp_done("CreateScaledBitmaps");
1418 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1421 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1424 void CreateBitmapTextures(Bitmap **bitmaps)
1426 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1429 void FreeBitmapTextures(Bitmap **bitmaps)
1431 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1434 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1436 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1440 // ----------------------------------------------------------------------------
1441 // mouse pointer functions
1442 // ----------------------------------------------------------------------------
1444 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1446 // XPM image definitions
1447 static const char *cursor_image_none[] =
1449 // width height num_colors chars_per_pixel
1479 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1480 static const char *cursor_image_dot[] =
1482 // width height num_colors chars_per_pixel
1511 static const char **cursor_image_playfield = cursor_image_dot;
1513 // some people complained about a "white dot" on the screen and thought it
1514 // was a graphical error... OK, let's just remove the whole pointer :-)
1515 static const char **cursor_image_playfield = cursor_image_none;
1518 static const int cursor_bit_order = BIT_ORDER_MSB;
1520 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1522 struct MouseCursorInfo *cursor;
1523 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1524 int header_lines = 4;
1527 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1529 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1532 for (y = 0; y < cursor->width; y++)
1534 for (x = 0; x < cursor->height; x++)
1537 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1542 cursor->data[i] = cursor->mask[i] = 0;
1545 switch (image[header_lines + y][x])
1548 cursor->data[i] |= bit_mask;
1549 cursor->mask[i] |= bit_mask;
1553 cursor->mask[i] |= bit_mask;
1562 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1567 void SetMouseCursor(int mode)
1569 static struct MouseCursorInfo *cursor_none = NULL;
1570 static struct MouseCursorInfo *cursor_playfield = NULL;
1571 struct MouseCursorInfo *cursor_new;
1572 int mode_final = mode;
1574 if (cursor_none == NULL)
1575 cursor_none = get_cursor_from_image(cursor_image_none);
1577 if (cursor_playfield == NULL)
1578 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1580 if (gfx.cursor_mode_override != CURSOR_UNDEFINED)
1581 mode_final = gfx.cursor_mode_override;
1583 cursor_new = (mode_final == CURSOR_DEFAULT ? NULL :
1584 mode_final == CURSOR_NONE ? cursor_none :
1585 mode_final == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1587 SDLSetMouseCursor(cursor_new);
1589 gfx.cursor_mode = mode;
1590 gfx.cursor_mode_final = mode_final;
1593 void UpdateRawMousePosition(int mouse_x, int mouse_y)
1595 // mouse events do not contain logical screen size corrections yet
1596 SDLCorrectRawMousePosition(&mouse_x, &mouse_y);
1598 mouse_x -= video.screen_xoffset;
1599 mouse_y -= video.screen_yoffset;
1601 gfx.mouse_x = mouse_x;
1602 gfx.mouse_y = mouse_y;
1605 void UpdateMousePosition(void)
1607 int mouse_x, mouse_y;
1610 SDL_GetMouseState(&mouse_x, &mouse_y);
1612 UpdateRawMousePosition(mouse_x, mouse_y);
1616 // ============================================================================
1618 // ============================================================================
1620 void OpenAudio(void)
1622 // always start with reliable default values
1623 audio.sound_available = FALSE;
1624 audio.music_available = FALSE;
1625 audio.loops_available = FALSE;
1627 audio.sound_enabled = FALSE;
1628 audio.sound_deactivated = FALSE;
1630 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1631 audio.mixer_pid = 0;
1632 audio.device_name = NULL;
1633 audio.device_fd = -1;
1635 audio.num_channels = 0;
1636 audio.music_channel = 0;
1637 audio.first_sound_channel = 0;
1642 void CloseAudio(void)
1646 audio.sound_enabled = FALSE;
1649 void SetAudioMode(boolean enabled)
1651 if (!audio.sound_available)
1654 audio.sound_enabled = enabled;
1658 // ============================================================================
1660 // ============================================================================
1662 void InitEventFilter(EventFilter filter_function)
1664 SDL_SetEventFilter(filter_function, NULL);
1667 boolean PendingEvent(void)
1669 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1672 void WaitEvent(Event *event)
1674 SDLWaitEvent(event);
1677 void PeekEvent(Event *event)
1679 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1682 void PumpEvents(void)
1687 void CheckQuitEvent(void)
1689 if (SDL_QuitRequested())
1690 program.exit_function(0);
1693 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1695 // key up/down events in SDL2 do not return text characters anymore
1696 return event->keysym.sym;
1699 KeyMod HandleKeyModState(Key key, int key_status)
1701 static KeyMod current_modifiers = KMOD_None;
1703 if (key != KSYM_UNDEFINED) // new key => check for modifier key change
1705 KeyMod new_modifier = KMOD_None;
1710 new_modifier = KMOD_Shift_L;
1713 new_modifier = KMOD_Shift_R;
1715 case KSYM_Control_L:
1716 new_modifier = KMOD_Control_L;
1718 case KSYM_Control_R:
1719 new_modifier = KMOD_Control_R;
1722 new_modifier = KMOD_Meta_L;
1725 new_modifier = KMOD_Meta_R;
1728 new_modifier = KMOD_Alt_L;
1731 new_modifier = KMOD_Alt_R;
1737 if (key_status == KEY_PRESSED)
1738 current_modifiers |= new_modifier;
1740 current_modifiers &= ~new_modifier;
1743 return current_modifiers;
1746 KeyMod GetKeyModState(void)
1748 return (KeyMod)SDL_GetModState();
1751 KeyMod GetKeyModStateFromEvents(void)
1753 /* always use key modifier state as tracked from key events (this is needed
1754 if the modifier key event was injected into the event queue, but the key
1755 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1756 query the keys as held pressed on the keyboard) -- this case is currently
1757 only used to filter out clipboard insert events from "True X-Mouse" tool */
1759 return HandleKeyModState(KSYM_UNDEFINED, 0);
1762 void StartTextInput(int x, int y, int width, int height)
1764 textinput_status = TRUE;
1766 #if defined(HAS_SCREEN_KEYBOARD)
1767 SDL_StartTextInput();
1769 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1771 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1772 video.shifted_up_delay = SDL_GetTicks();
1773 video.shifted_up = TRUE;
1778 void StopTextInput(void)
1780 textinput_status = FALSE;
1782 #if defined(HAS_SCREEN_KEYBOARD)
1783 SDL_StopTextInput();
1785 if (video.shifted_up)
1787 video.shifted_up_pos = 0;
1788 video.shifted_up_delay = SDL_GetTicks();
1789 video.shifted_up = FALSE;
1794 void PushUserEvent(int code, int value1, int value2)
1798 SDL_memset(&event, 0, sizeof(event));
1800 event.type = EVENT_USER;
1802 event.value1 = value1;
1803 event.value2 = value2;
1805 SDL_PushEvent((SDL_Event *)&event);
1809 // ============================================================================
1810 // joystick functions
1811 // ============================================================================
1813 void InitJoysticks(void)
1817 #if defined(NO_JOYSTICK)
1818 return; // joysticks generally deactivated by compile-time directive
1821 // always start with reliable default values
1822 joystick.status = JOYSTICK_NOT_AVAILABLE;
1823 for (i = 0; i < MAX_PLAYERS; i++)
1824 joystick.nr[i] = -1; // no joystick configured
1829 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1831 return SDLReadJoystick(nr, x, y, b1, b2);
1834 boolean CheckJoystickOpened(int nr)
1836 return SDLCheckJoystickOpened(nr);
1839 void ClearJoystickState(void)
1841 SDLClearJoystickState();