1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
19 #define ENABLE_UNUSED_CODE 0 // currently unused functions
21 #define DEBUG_JOYSTICKS 0
24 // ============================================================================
26 // ============================================================================
28 // SDL internal variables
29 static SDL_Window *sdl_window = NULL;
30 static SDL_Renderer *sdl_renderer = NULL;
31 static SDL_Texture *sdl_texture_stream = NULL;
32 static SDL_Texture *sdl_texture_target = NULL;
33 static boolean fullscreen_enabled = FALSE;
34 static boolean limit_screen_updates = FALSE;
37 // functions from SGE library
38 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
40 #if defined(USE_TOUCH_INPUT_OVERLAY)
41 // functions to draw overlay graphics for touch device input
42 static void DrawTouchInputOverlay(void);
43 static void DrawTouchGadgetsOverlay(void);
46 void SDLLimitScreenUpdates(boolean enable)
48 limit_screen_updates = enable;
51 static void FinalizeScreen(int draw_target)
53 // copy global animations to render target buffer, if defined (below border)
54 if (gfx.draw_global_anim_function != NULL)
55 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
57 // copy global masked border to render target buffer, if defined
58 if (gfx.draw_global_border_function != NULL)
59 gfx.draw_global_border_function(draw_target);
61 // copy global animations to render target buffer, if defined (above border)
62 if (gfx.draw_global_anim_function != NULL)
63 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
65 // copy tile selection cursor to render target buffer, if defined (above all)
66 if (gfx.draw_tile_cursor_function != NULL)
67 gfx.draw_tile_cursor_function(draw_target);
70 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
72 static unsigned int update_screen_delay = 0;
73 unsigned int update_screen_delay_value = 50; // (milliseconds)
74 SDL_Surface *screen = backbuffer->surface;
76 if (limit_screen_updates &&
77 !DelayReached(&update_screen_delay, update_screen_delay_value))
80 LimitScreenUpdates(FALSE);
84 static int LastFrameCounter = 0;
85 boolean changed = (FrameCounter != LastFrameCounter);
87 printf("::: FrameCounter == %d [%s]\n", FrameCounter,
88 (changed ? "-" : "SAME FRAME UPDATED"));
90 LastFrameCounter = FrameCounter;
99 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
100 gfx.final_screen_bitmap != NULL) // may not be initialized yet
102 // draw global animations using bitmaps instead of using textures
103 // to prevent texture scaling artefacts (this is potentially slower)
105 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
106 gfx.win_xsize, gfx.win_ysize, 0, 0);
108 FinalizeScreen(DRAW_TO_SCREEN);
110 screen = gfx.final_screen_bitmap->surface;
112 // force full window redraw
116 SDL_Texture *sdl_texture = sdl_texture_stream;
118 // deactivate use of target texture if render targets are not supported
119 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
120 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
121 sdl_texture_target == NULL)
122 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
124 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
125 sdl_texture = sdl_texture_target;
129 int bytes_x = screen->pitch / video.width;
130 int bytes_y = screen->pitch;
132 SDL_UpdateTexture(sdl_texture, rect,
133 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
138 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
141 int xoff = video.screen_xoffset;
142 int yoff = video.screen_yoffset;
143 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
144 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
145 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
147 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
148 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
149 dst_rect2 = &dst_rect_screen;
151 dst_rect1 = &dst_rect_screen;
153 #if defined(HAS_SCREEN_KEYBOARD)
154 if (video.shifted_up || video.shifted_up_delay)
156 int time_current = SDL_GetTicks();
157 int pos = video.shifted_up_pos;
158 int pos_last = video.shifted_up_pos_last;
160 if (!DelayReachedExt(&video.shifted_up_delay, video.shifted_up_delay_value,
163 int delay = time_current - video.shifted_up_delay;
164 int delay_value = video.shifted_up_delay_value;
166 pos = pos_last + (pos - pos_last) * delay / delay_value;
170 video.shifted_up_pos_last = pos;
171 video.shifted_up_delay = 0;
174 SDL_Rect src_rect_up = { 0, pos, video.width, video.height - pos };
175 SDL_Rect dst_rect_up = { xoff, yoff, video.width, video.height - pos };
177 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
178 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
180 src_rect2 = &src_rect_up;
181 dst_rect2 = &dst_rect_up;
185 src_rect1 = &src_rect_up;
186 dst_rect1 = &dst_rect_up;
191 // clear render target buffer
192 SDL_RenderClear(sdl_renderer);
194 // set renderer to use target texture for rendering
195 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
196 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
197 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
199 // copy backbuffer texture to render target buffer
200 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
201 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
203 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
204 FinalizeScreen(DRAW_TO_SCREEN);
206 // when using target texture, copy it to screen buffer
207 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
208 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
210 SDL_SetRenderTarget(sdl_renderer, NULL);
211 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
214 #if defined(USE_TOUCH_INPUT_OVERLAY)
215 // draw overlay graphics for touch device input, if needed
216 DrawTouchInputOverlay();
218 // draw overlay gadgets for touch device input, if needed
219 DrawTouchGadgetsOverlay();
222 // global synchronization point of the game to align video frame delay
223 if (with_frame_delay)
224 WaitUntilDelayReached(&video.frame_delay, video.frame_delay_value);
226 video.frame_counter++;
228 // show render target buffer on screen
229 SDL_RenderPresent(sdl_renderer);
232 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
234 PumpEvents(); // execute event filter actions while waiting
236 UpdateScreenExt(rect, TRUE);
239 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
241 UpdateScreenExt(rect, FALSE);
244 void Delay_WithScreenUpdates(unsigned int delay)
246 unsigned int time_current = SDL_GetTicks();
247 unsigned int time_delayed = time_current + delay;
249 while (time_current < time_delayed)
251 // updating the screen contains waiting for frame delay (non-busy)
252 UpdateScreen_WithFrameDelay(NULL);
254 time_current = SDL_GetTicks();
258 static void SDLSetWindowIcon(char *basename)
260 // (setting the window icon on Mac OS X would replace the high-quality
261 // dock icon with the currently smaller (and uglier) icon from file)
263 #if !defined(PLATFORM_MACOSX)
264 char *filename = getCustomImageFilename(basename);
265 SDL_Surface *surface;
267 if (filename == NULL)
269 Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
274 if ((surface = IMG_Load(filename)) == NULL)
276 Error(ERR_WARN, "IMG_Load('%s') failed: %s", basename, SDL_GetError());
281 // set transparent color
282 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
283 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
285 SDL_SetWindowIcon(sdl_window, surface);
289 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
290 SDL_PixelFormat *format2)
292 return (format1->format == format2->format &&
293 format1->BitsPerPixel == format2->BitsPerPixel &&
294 format1->BytesPerPixel == format2->BytesPerPixel &&
295 format1->Rmask == format2->Rmask &&
296 format1->Gmask == format2->Gmask &&
297 format1->Bmask == format2->Bmask);
300 static Pixel SDLGetColorKey(SDL_Surface *surface)
304 if (SDL_GetColorKey(surface, &color_key) != 0)
310 static boolean SDLHasColorKey(SDL_Surface *surface)
312 return (SDLGetColorKey(surface) != -1);
315 static boolean SDLHasAlpha(SDL_Surface *surface)
317 SDL_BlendMode blend_mode;
319 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
322 return (blend_mode == SDL_BLENDMODE_BLEND);
325 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
327 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
329 SDL_SetSurfaceBlendMode(surface, blend_mode);
330 SDL_SetSurfaceAlphaMod(surface, alpha);
333 const char *SDLGetRendererName(void)
335 static SDL_RendererInfo renderer_info;
337 SDL_GetRendererInfo(sdl_renderer, &renderer_info);
339 return renderer_info.name;
342 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
344 SDL_PixelFormat format;
345 SDL_Surface *new_surface;
350 if (backbuffer && backbuffer->surface)
352 format = *backbuffer->surface->format;
353 format.Amask = surface->format->Amask; // keep alpha channel
357 format = *surface->format;
360 new_surface = SDL_ConvertSurface(surface, &format, 0);
362 if (new_surface == NULL)
363 Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
365 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
366 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
367 SDL_SetColorKey(new_surface, SET_TRANSPARENT_PIXEL,
368 SDLGetColorKey(surface));
373 boolean SDLSetNativeSurface(SDL_Surface **surface)
375 SDL_Surface *new_surface;
377 if (surface == NULL ||
379 backbuffer == NULL ||
380 backbuffer->surface == NULL)
383 // if pixel format already optimized for destination surface, do nothing
384 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
387 new_surface = SDLGetNativeSurface(*surface);
389 SDL_FreeSurface(*surface);
391 *surface = new_surface;
396 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
398 if (program.headless)
401 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
404 Error(ERR_EXIT, "SDL_CreateTextureFromSurface() failed: %s",
410 void SDLCreateBitmapTextures(Bitmap *bitmap)
416 SDL_DestroyTexture(bitmap->texture);
417 if (bitmap->texture_masked)
418 SDL_DestroyTexture(bitmap->texture_masked);
420 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
421 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
424 void SDLFreeBitmapTextures(Bitmap *bitmap)
430 SDL_DestroyTexture(bitmap->texture);
431 if (bitmap->texture_masked)
432 SDL_DestroyTexture(bitmap->texture_masked);
434 bitmap->texture = NULL;
435 bitmap->texture_masked = NULL;
438 void SDLInitVideoDisplay(void)
440 // initialize SDL video
441 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
442 Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
444 // set default SDL depth
445 video.default_depth = 32; // (how to determine video depth in SDL2?)
447 // Code used with SDL 1.2:
448 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
451 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
453 if (program.headless)
456 video.window_scaling_percent = setup.window_scaling_percent;
457 video.window_scaling_quality = setup.window_scaling_quality;
459 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
461 // SDL 2.0: support for (desktop) fullscreen mode available
462 video.fullscreen_available = TRUE;
464 // open SDL video output device (window or fullscreen mode)
465 if (!SDLSetVideoMode(fullscreen))
466 Error(ERR_EXIT, "setting video mode failed");
468 // !!! SDL2 can only set the window icon if the window already exists !!!
470 SDLSetWindowIcon(program.icon_filename);
472 // set window and icon title
476 static void SDLInitVideoBuffer_DrawBuffer(void)
478 /* SDL cannot directly draw to the visible video framebuffer like X11,
479 but always uses a backbuffer, which is then blitted to the visible
480 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
481 visible video framebuffer with 'SDL_Flip', if the hardware supports
482 this). Therefore do not use an additional backbuffer for drawing, but
483 use a symbolic buffer (distinguishable from the SDL backbuffer) called
484 'window', which indicates that the SDL backbuffer should be updated to
485 the visible video framebuffer when attempting to blit to it.
487 For convenience, it seems to be a good idea to create this symbolic
488 buffer 'window' at the same size as the SDL backbuffer. Although it
489 should never be drawn to directly, it would do no harm nevertheless. */
491 // create additional (symbolic) buffer for double-buffering
492 ReCreateBitmap(&window, video.width, video.height);
494 // create dummy drawing buffer for headless mode, if needed
495 if (program.headless)
496 ReCreateBitmap(&backbuffer, video.width, video.height);
499 void SDLInitVideoBuffer(boolean fullscreen)
501 SDLInitVideoBuffer_VideoBuffer(fullscreen);
502 SDLInitVideoBuffer_DrawBuffer();
505 static boolean SDLCreateScreen(boolean fullscreen)
507 SDL_Surface *new_surface = NULL;
509 int surface_flags_window = SURFACE_FLAGS;
510 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
513 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
515 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
516 _without_ enabling 2D/3D acceleration and/or guest additions installed,
517 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
518 it will try to use accelerated graphics and apparently fails miserably) */
519 int renderer_flags = SDL_RENDERER_SOFTWARE;
522 int width = video.width;
523 int height = video.height;
524 int screen_width = video.screen_width;
525 int screen_height = video.screen_height;
526 int surface_flags = (fullscreen ? surface_flags_fullscreen :
527 surface_flags_window);
529 // default window size is unscaled
530 video.window_width = screen_width;
531 video.window_height = screen_height;
533 // store if initial screen mode is fullscreen mode when changing screen size
534 video.fullscreen_initial = fullscreen;
536 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
538 video.window_width = window_scaling_factor * screen_width;
539 video.window_height = window_scaling_factor * screen_height;
541 if (sdl_texture_stream)
543 SDL_DestroyTexture(sdl_texture_stream);
544 sdl_texture_stream = NULL;
547 if (sdl_texture_target)
549 SDL_DestroyTexture(sdl_texture_target);
550 sdl_texture_target = NULL;
553 if (!(fullscreen && fullscreen_enabled))
557 SDL_DestroyRenderer(sdl_renderer);
563 SDL_DestroyWindow(sdl_window);
568 if (sdl_window == NULL)
569 sdl_window = SDL_CreateWindow(program.window_title,
570 SDL_WINDOWPOS_CENTERED,
571 SDL_WINDOWPOS_CENTERED,
576 if (sdl_window != NULL)
578 if (sdl_renderer == NULL)
579 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
581 if (sdl_renderer != NULL)
583 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
584 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
586 SDLSetScreenVsyncMode(setup.vsync_mode);
588 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
589 SDL_PIXELFORMAT_ARGB8888,
590 SDL_TEXTUREACCESS_STREAMING,
593 if (SDL_RenderTargetSupported(sdl_renderer))
594 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
595 SDL_PIXELFORMAT_ARGB8888,
596 SDL_TEXTUREACCESS_TARGET,
599 if (sdl_texture_stream != NULL)
601 // use SDL default values for RGB masks and no alpha channel
602 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
604 if (new_surface == NULL)
605 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
609 Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
614 Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
619 Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
622 SDLSetScreenProperties();
624 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
625 if (new_surface != NULL)
626 fullscreen_enabled = fullscreen;
628 if (backbuffer == NULL)
629 backbuffer = CreateBitmapStruct();
631 backbuffer->width = video.width;
632 backbuffer->height = video.height;
634 if (backbuffer->surface)
635 SDL_FreeSurface(backbuffer->surface);
637 backbuffer->surface = new_surface;
639 return (new_surface != NULL);
642 boolean SDLSetVideoMode(boolean fullscreen)
644 boolean success = FALSE;
648 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
650 // switch display to fullscreen mode, if available
651 success = SDLCreateScreen(TRUE);
655 // switching display to fullscreen mode failed -- do not try it again
656 video.fullscreen_available = FALSE;
660 video.fullscreen_enabled = TRUE;
664 if ((!fullscreen && video.fullscreen_enabled) || !success)
666 // switch display to window mode
667 success = SDLCreateScreen(FALSE);
671 // switching display to window mode failed -- should not happen
675 video.fullscreen_enabled = FALSE;
676 video.window_scaling_percent = setup.window_scaling_percent;
677 video.window_scaling_quality = setup.window_scaling_quality;
679 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
683 SDLRedrawWindow(); // map window
688 void SDLSetWindowTitle(void)
690 if (sdl_window == NULL)
693 SDL_SetWindowTitle(sdl_window, program.window_title);
696 void SDLSetWindowScaling(int window_scaling_percent)
698 if (sdl_window == NULL)
701 float window_scaling_factor = (float)window_scaling_percent / 100;
702 int new_window_width = (int)(window_scaling_factor * video.screen_width);
703 int new_window_height = (int)(window_scaling_factor * video.screen_height);
705 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
707 video.window_scaling_percent = window_scaling_percent;
708 video.window_width = new_window_width;
709 video.window_height = new_window_height;
714 void SDLSetWindowScalingQuality(char *window_scaling_quality)
716 SDL_Texture *new_texture;
718 if (sdl_texture_stream == NULL)
721 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
723 new_texture = SDL_CreateTexture(sdl_renderer,
724 SDL_PIXELFORMAT_ARGB8888,
725 SDL_TEXTUREACCESS_STREAMING,
726 video.width, video.height);
728 if (new_texture != NULL)
730 SDL_DestroyTexture(sdl_texture_stream);
732 sdl_texture_stream = new_texture;
735 if (SDL_RenderTargetSupported(sdl_renderer))
736 new_texture = SDL_CreateTexture(sdl_renderer,
737 SDL_PIXELFORMAT_ARGB8888,
738 SDL_TEXTUREACCESS_TARGET,
739 video.width, video.height);
743 if (new_texture != NULL)
745 SDL_DestroyTexture(sdl_texture_target);
747 sdl_texture_target = new_texture;
752 video.window_scaling_quality = window_scaling_quality;
755 void SDLSetWindowFullscreen(boolean fullscreen)
757 if (sdl_window == NULL)
760 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
762 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
763 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
765 // if screen size was changed in fullscreen mode, correct desktop window size
766 if (!fullscreen && video.fullscreen_initial)
768 SDLSetWindowScaling(setup.window_scaling_percent);
769 SDL_SetWindowPosition(sdl_window,
770 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
772 video.fullscreen_initial = FALSE;
776 void SDLSetDisplaySize(void)
778 if (sdl_renderer != NULL)
782 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
784 video.display_width = w;
785 video.display_height = h;
788 Error(ERR_DEBUG, "SDL renderer size: %d x %d",
789 video.display_width, video.display_height);
794 SDL_Rect display_bounds;
796 SDL_GetDisplayBounds(0, &display_bounds);
798 video.display_width = display_bounds.w;
799 video.display_height = display_bounds.h;
802 Error(ERR_DEBUG, "SDL display size: %d x %d",
803 video.display_width, video.display_height);
808 void SDLSetScreenSizeAndOffsets(int width, int height)
810 // set default video screen size and offsets
811 video.screen_width = width;
812 video.screen_height = height;
813 video.screen_xoffset = 0;
814 video.screen_yoffset = 0;
816 #if defined(USE_COMPLETE_DISPLAY)
817 float ratio_video = (float) width / height;
818 float ratio_display = (float) video.display_width / video.display_height;
820 if (ratio_video != ratio_display)
822 // adjust drawable screen size to cover the whole device display
824 if (ratio_video < ratio_display)
825 video.screen_width *= ratio_display / ratio_video;
827 video.screen_height *= ratio_video / ratio_display;
829 video.screen_xoffset = (video.screen_width - width) / 2;
830 video.screen_yoffset = (video.screen_height - height) / 2;
833 Error(ERR_DEBUG, "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
835 video.screen_width, video.screen_height,
836 ratio_video, ratio_display);
842 void SDLSetScreenSizeForRenderer(int width, int height)
844 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
847 void SDLSetScreenProperties(void)
850 SDLSetScreenSizeAndOffsets(video.width, video.height);
851 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
854 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
856 video.screen_rendering_mode =
857 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
858 SPECIAL_RENDERING_BITMAP :
859 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
860 SPECIAL_RENDERING_TARGET:
861 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
862 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
865 void SDLSetScreenVsyncMode(char *vsync_mode)
868 (strEqual(vsync_mode, STR_VSYNC_MODE_NORMAL) ? VSYNC_MODE_NORMAL :
869 strEqual(vsync_mode, STR_VSYNC_MODE_ADAPTIVE) ? VSYNC_MODE_ADAPTIVE :
871 int result = SDL_GL_SetSwapInterval(interval);
873 // if adaptive vsync requested, but not supported, retry with normal vsync
874 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
875 SDL_GL_SetSwapInterval(VSYNC_MODE_NORMAL);
878 void SDLRedrawWindow(void)
880 UpdateScreen_WithoutFrameDelay(NULL);
883 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
886 if (program.headless)
889 SDL_Surface *surface =
890 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
893 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
895 SDLSetNativeSurface(&surface);
897 bitmap->surface = surface;
900 void SDLFreeBitmapPointers(Bitmap *bitmap)
903 SDL_FreeSurface(bitmap->surface);
904 if (bitmap->surface_masked)
905 SDL_FreeSurface(bitmap->surface_masked);
907 bitmap->surface = NULL;
908 bitmap->surface_masked = NULL;
911 SDL_DestroyTexture(bitmap->texture);
912 if (bitmap->texture_masked)
913 SDL_DestroyTexture(bitmap->texture_masked);
915 bitmap->texture = NULL;
916 bitmap->texture_masked = NULL;
919 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
920 int src_x, int src_y, int width, int height,
921 int dst_x, int dst_y, int mask_mode)
923 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
924 SDL_Rect src_rect, dst_rect;
936 // if (src_bitmap != backbuffer || dst_bitmap != window)
937 if (!(src_bitmap == backbuffer && dst_bitmap == window))
938 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
939 src_bitmap->surface_masked : src_bitmap->surface),
940 &src_rect, real_dst_bitmap->surface, &dst_rect);
942 if (dst_bitmap == window)
943 UpdateScreen_WithFrameDelay(&dst_rect);
946 void SDLBlitTexture(Bitmap *bitmap,
947 int src_x, int src_y, int width, int height,
948 int dst_x, int dst_y, int mask_mode)
950 SDL_Texture *texture;
955 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
970 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
973 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
976 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
984 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
986 if (dst_bitmap == window)
987 UpdateScreen_WithFrameDelay(&rect);
990 void PrepareFadeBitmap(int draw_target)
992 Bitmap *fade_bitmap =
993 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
994 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
996 if (fade_bitmap == NULL)
999 // copy backbuffer to fading buffer
1000 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1002 // add border and animations to fading buffer
1003 FinalizeScreen(draw_target);
1006 void SDLFadeRectangle(int x, int y, int width, int height,
1007 int fade_mode, int fade_delay, int post_delay,
1008 void (*draw_border_function)(void))
1010 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1011 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1012 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1013 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1014 SDL_Surface *surface_screen = backbuffer->surface;
1015 SDL_Rect src_rect, dst_rect;
1017 int src_x = x, src_y = y;
1018 int dst_x = x, dst_y = y;
1019 unsigned int time_last, time_current;
1021 // store function for drawing global masked border
1022 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1024 // deactivate drawing of global border while fading, if needed
1025 if (draw_border_function == NULL)
1026 gfx.draw_global_border_function = NULL;
1031 src_rect.h = height;
1035 dst_rect.w = width; // (ignored)
1036 dst_rect.h = height; // (ignored)
1038 dst_rect2 = dst_rect;
1040 // before fading in, store backbuffer (without animation graphics)
1041 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1042 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1044 // copy source and target surfaces to temporary surfaces for fading
1045 if (fade_mode & FADE_TYPE_TRANSFORM)
1047 // (source and target fading buffer already prepared)
1049 else if (fade_mode & FADE_TYPE_FADE_IN)
1051 // (target fading buffer already prepared)
1052 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1054 else // FADE_TYPE_FADE_OUT
1056 // (source fading buffer already prepared)
1057 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1060 time_current = SDL_GetTicks();
1062 if (fade_delay <= 0)
1064 // immediately draw final target frame without delay
1065 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1069 // when fading without delay, also skip post delay
1073 if (fade_mode == FADE_MODE_MELT)
1075 boolean done = FALSE;
1076 int melt_pixels = 2;
1077 int melt_columns = width / melt_pixels;
1078 int ypos[melt_columns];
1079 int max_steps = height / 8 + 32;
1084 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1086 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1088 ypos[0] = -GetSimpleRandom(16);
1090 for (i = 1 ; i < melt_columns; i++)
1092 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1094 ypos[i] = ypos[i - 1] + r;
1107 time_last = time_current;
1108 time_current = SDL_GetTicks();
1109 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1110 steps_final = MIN(MAX(0, steps), max_steps);
1114 done = (steps_done >= steps_final);
1116 for (i = 0 ; i < melt_columns; i++)
1124 else if (ypos[i] < height)
1129 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1131 if (ypos[i] + dy >= height)
1132 dy = height - ypos[i];
1134 // copy part of (appearing) target surface to upper area
1135 src_rect.x = src_x + i * melt_pixels;
1136 // src_rect.y = src_y + ypos[i];
1138 src_rect.w = melt_pixels;
1140 src_rect.h = ypos[i] + dy;
1142 dst_rect.x = dst_x + i * melt_pixels;
1143 // dst_rect.y = dst_y + ypos[i];
1146 if (steps_done >= steps_final)
1147 SDL_BlitSurface(surface_target, &src_rect,
1148 surface_screen, &dst_rect);
1152 // copy part of (disappearing) source surface to lower area
1153 src_rect.x = src_x + i * melt_pixels;
1155 src_rect.w = melt_pixels;
1156 src_rect.h = height - ypos[i];
1158 dst_rect.x = dst_x + i * melt_pixels;
1159 dst_rect.y = dst_y + ypos[i];
1161 if (steps_done >= steps_final)
1162 SDL_BlitSurface(surface_source, &src_rect,
1163 surface_screen, &dst_rect);
1169 src_rect.x = src_x + i * melt_pixels;
1171 src_rect.w = melt_pixels;
1172 src_rect.h = height;
1174 dst_rect.x = dst_x + i * melt_pixels;
1177 if (steps_done >= steps_final)
1178 SDL_BlitSurface(surface_target, &src_rect,
1179 surface_screen, &dst_rect);
1183 if (steps_done >= steps_final)
1185 if (draw_border_function != NULL)
1186 draw_border_function();
1188 UpdateScreen_WithFrameDelay(&dst_rect2);
1192 else if (fade_mode == FADE_MODE_CURTAIN)
1196 int xx_size = width / 2;
1198 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1200 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1202 for (xx = 0; xx < xx_size;)
1204 time_last = time_current;
1205 time_current = SDL_GetTicks();
1206 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1207 xx_final = MIN(MAX(0, xx), xx_size);
1212 src_rect.h = height;
1217 // draw new (target) image to screen buffer
1218 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1220 if (xx_final < xx_size)
1222 src_rect.w = xx_size - xx_final;
1223 src_rect.h = height;
1225 // draw old (source) image to screen buffer (left side)
1227 src_rect.x = src_x + xx_final;
1230 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1232 // draw old (source) image to screen buffer (right side)
1234 src_rect.x = src_x + xx_size;
1235 dst_rect.x = dst_x + xx_size + xx_final;
1237 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1240 if (draw_border_function != NULL)
1241 draw_border_function();
1243 // only update the region of the screen that is affected from fading
1244 UpdateScreen_WithFrameDelay(&dst_rect2);
1247 else // fading in, fading out or cross-fading
1252 for (alpha = 0.0; alpha < 255.0;)
1254 time_last = time_current;
1255 time_current = SDL_GetTicks();
1256 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1257 alpha_final = MIN(MAX(0, alpha), 255);
1259 // draw existing (source) image to screen buffer
1260 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1262 // draw new (target) image to screen buffer using alpha blending
1263 SDLSetAlpha(surface_target, TRUE, alpha_final);
1264 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1266 if (draw_border_function != NULL)
1267 draw_border_function();
1269 // only update the region of the screen that is affected from fading
1270 UpdateScreen_WithFrameDelay(&dst_rect);
1275 Delay_WithScreenUpdates(post_delay);
1277 // restore function for drawing global masked border
1278 gfx.draw_global_border_function = draw_global_border_function;
1280 // after fading in, restore backbuffer (without animation graphics)
1281 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1282 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1285 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1286 int to_x, int to_y, Uint32 color)
1288 SDL_Surface *surface = dst_bitmap->surface;
1292 swap_numbers(&from_x, &to_x);
1295 swap_numbers(&from_y, &to_y);
1299 rect.w = (to_x - from_x + 1);
1300 rect.h = (to_y - from_y + 1);
1302 SDL_FillRect(surface, &rect, color);
1305 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1306 int to_x, int to_y, Uint32 color)
1308 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1311 #if ENABLE_UNUSED_CODE
1312 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1313 int num_points, Uint32 color)
1318 for (i = 0; i < num_points - 1; i++)
1320 for (x = 0; x < line_width; x++)
1322 for (y = 0; y < line_width; y++)
1324 int dx = x - line_width / 2;
1325 int dy = y - line_width / 2;
1327 if ((x == 0 && y == 0) ||
1328 (x == 0 && y == line_width - 1) ||
1329 (x == line_width - 1 && y == 0) ||
1330 (x == line_width - 1 && y == line_width - 1))
1333 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1334 points[i+1].x + dx, points[i+1].y + dy, color);
1341 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1343 SDL_Surface *surface = src_bitmap->surface;
1345 switch (surface->format->BytesPerPixel)
1347 case 1: // assuming 8-bpp
1349 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1353 case 2: // probably 15-bpp or 16-bpp
1355 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1359 case 3: // slow 24-bpp mode; usually not used
1362 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1366 shift = surface->format->Rshift;
1367 color |= *(pix + shift / 8) >> shift;
1368 shift = surface->format->Gshift;
1369 color |= *(pix + shift / 8) >> shift;
1370 shift = surface->format->Bshift;
1371 color |= *(pix + shift / 8) >> shift;
1377 case 4: // probably 32-bpp
1379 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1388 // ============================================================================
1389 // The following functions were taken from the SGE library
1390 // (SDL Graphics Extension Library) by Anders Lindström
1391 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1392 // ============================================================================
1394 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1396 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1398 switch (surface->format->BytesPerPixel)
1403 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1409 // Probably 15-bpp or 16-bpp
1410 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1416 // Slow 24-bpp mode, usually not used
1420 // Gack - slow, but endian correct
1421 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1422 shift = surface->format->Rshift;
1423 *(pix+shift/8) = color>>shift;
1424 shift = surface->format->Gshift;
1425 *(pix+shift/8) = color>>shift;
1426 shift = surface->format->Bshift;
1427 *(pix+shift/8) = color>>shift;
1434 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1442 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1443 Uint8 R, Uint8 G, Uint8 B)
1445 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1448 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1450 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1453 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1455 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1458 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1463 // Gack - slow, but endian correct
1464 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1465 shift = surface->format->Rshift;
1466 *(pix+shift/8) = color>>shift;
1467 shift = surface->format->Gshift;
1468 *(pix+shift/8) = color>>shift;
1469 shift = surface->format->Bshift;
1470 *(pix+shift/8) = color>>shift;
1473 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1475 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1478 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1480 switch (dest->format->BytesPerPixel)
1483 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1487 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1491 _PutPixel24(dest,x,y,color);
1495 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1501 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1503 if (SDL_MUSTLOCK(surface))
1505 if (SDL_LockSurface(surface) < 0)
1511 _PutPixel(surface, x, y, color);
1513 if (SDL_MUSTLOCK(surface))
1515 SDL_UnlockSurface(surface);
1520 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1521 Uint8 r, Uint8 g, Uint8 b)
1523 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1526 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1528 if (y >= 0 && y <= dest->h - 1)
1530 switch (dest->format->BytesPerPixel)
1533 return y*dest->pitch;
1537 return y*dest->pitch/2;
1541 return y*dest->pitch;
1545 return y*dest->pitch/4;
1553 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1556 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1558 switch (surface->format->BytesPerPixel)
1563 *((Uint8 *)surface->pixels + ypitch + x) = color;
1569 // Probably 15-bpp or 16-bpp
1570 *((Uint16 *)surface->pixels + ypitch + x) = color;
1576 // Slow 24-bpp mode, usually not used
1580 // Gack - slow, but endian correct
1581 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1582 shift = surface->format->Rshift;
1583 *(pix+shift/8) = color>>shift;
1584 shift = surface->format->Gshift;
1585 *(pix+shift/8) = color>>shift;
1586 shift = surface->format->Bshift;
1587 *(pix+shift/8) = color>>shift;
1594 *((Uint32 *)surface->pixels + ypitch + x) = color;
1601 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1606 if (SDL_MUSTLOCK(Surface))
1608 if (SDL_LockSurface(Surface) < 0)
1622 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1626 if (x2 > Surface->w - 1)
1627 x2 = Surface->w - 1;
1634 SDL_FillRect(Surface, &l, Color);
1636 if (SDL_MUSTLOCK(Surface))
1638 SDL_UnlockSurface(Surface);
1642 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1643 Uint8 R, Uint8 G, Uint8 B)
1645 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1648 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1661 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1665 if (x2 > Surface->w - 1)
1666 x2 = Surface->w - 1;
1673 SDL_FillRect(Surface, &l, Color);
1676 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1681 if (SDL_MUSTLOCK(Surface))
1683 if (SDL_LockSurface(Surface) < 0)
1697 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1701 if (y2 > Surface->h - 1)
1702 y2 = Surface->h - 1;
1709 SDL_FillRect(Surface, &l, Color);
1711 if (SDL_MUSTLOCK(Surface))
1713 SDL_UnlockSurface(Surface);
1717 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1718 Uint8 R, Uint8 G, Uint8 B)
1720 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1723 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1736 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1740 if (y2 > Surface->h - 1)
1741 y2 = Surface->h - 1;
1748 SDL_FillRect(Surface, &l, Color);
1752 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1753 Sint16 x2, Sint16 y2, Uint32 Color,
1754 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1757 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1762 sdx = (dx < 0) ? -1 : 1;
1763 sdy = (dy < 0) ? -1 : 1;
1775 for (x = 0; x < dx; x++)
1777 Callback(Surface, px, py, Color);
1791 for (y = 0; y < dy; y++)
1793 Callback(Surface, px, py, Color);
1808 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1809 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1810 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1813 sge_DoLine(Surface, X1, Y1, X2, Y2,
1814 SDL_MapRGB(Surface->format, R, G, B), Callback);
1818 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1821 if (SDL_MUSTLOCK(Surface))
1823 if (SDL_LockSurface(Surface) < 0)
1828 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1830 // unlock the display
1831 if (SDL_MUSTLOCK(Surface))
1833 SDL_UnlockSurface(Surface);
1838 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1839 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1841 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1845 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1847 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1851 // ----------------------------------------------------------------------------
1852 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1853 // ----------------------------------------------------------------------------
1855 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1856 int width, int height, Uint32 color)
1860 for (y = src_y; y < src_y + height; y++)
1862 for (x = src_x; x < src_x + width; x++)
1864 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1866 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1871 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1872 int src_x, int src_y, int width, int height,
1873 int dst_x, int dst_y)
1877 for (y = 0; y < height; y++)
1879 for (x = 0; x < width; x++)
1881 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1883 if (pixel != BLACK_PIXEL)
1884 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1890 // ============================================================================
1891 // The following functions were taken from the SDL_gfx library version 2.0.3
1892 // (Rotozoomer) by Andreas Schiffler
1893 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1894 // ============================================================================
1896 // ----------------------------------------------------------------------------
1899 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1900 // ----------------------------------------------------------------------------
1910 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1913 tColorRGBA *sp, *csp, *dp;
1917 sp = csp = (tColorRGBA *) src->pixels;
1918 dp = (tColorRGBA *) dst->pixels;
1919 dgap = dst->pitch - dst->w * 4;
1921 for (y = 0; y < dst->h; y++)
1925 for (x = 0; x < dst->w; x++)
1927 tColorRGBA *sp0 = sp;
1928 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1929 tColorRGBA *sp00 = &sp0[0];
1930 tColorRGBA *sp01 = &sp0[1];
1931 tColorRGBA *sp10 = &sp1[0];
1932 tColorRGBA *sp11 = &sp1[1];
1935 // create new color pixel from all four source color pixels
1936 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1937 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1938 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1939 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1944 // advance source pointers
1947 // advance destination pointer
1951 // advance source pointer
1952 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1954 // advance destination pointers
1955 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1961 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1963 int x, y, *sax, *say, *csax, *csay;
1965 tColorRGBA *sp, *csp, *csp0, *dp;
1968 // use specialized zoom function when scaling down to exactly half size
1969 if (src->w == 2 * dst->w &&
1970 src->h == 2 * dst->h)
1971 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1974 sx = (float) src->w / (float) dst->w;
1975 sy = (float) src->h / (float) dst->h;
1977 // allocate memory for row increments
1978 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1979 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1981 // precalculate row increments
1982 for (x = 0; x <= dst->w; x++)
1983 *csax++ = (int)(sx * x);
1985 for (y = 0; y <= dst->h; y++)
1986 *csay++ = (int)(sy * y);
1989 sp = csp = csp0 = (tColorRGBA *) src->pixels;
1990 dp = (tColorRGBA *) dst->pixels;
1991 dgap = dst->pitch - dst->w * 4;
1994 for (y = 0; y < dst->h; y++)
1999 for (x = 0; x < dst->w; x++)
2004 // advance source pointers
2008 // advance destination pointer
2012 // advance source pointer
2014 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2016 // advance destination pointers
2017 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2026 // ----------------------------------------------------------------------------
2029 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2030 // ----------------------------------------------------------------------------
2032 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2034 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2035 Uint8 *sp, *dp, *csp;
2039 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2040 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2042 // allocate memory for row increments
2043 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2044 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2046 // precalculate row increments
2049 for (x = 0; x < dst->w; x++)
2052 *csax = (csx >> 16);
2059 for (y = 0; y < dst->h; y++)
2062 *csay = (csy >> 16);
2069 for (x = 0; x < dst->w; x++)
2077 for (y = 0; y < dst->h; y++)
2084 sp = csp = (Uint8 *) src->pixels;
2085 dp = (Uint8 *) dst->pixels;
2086 dgap = dst->pitch - dst->w;
2090 for (y = 0; y < dst->h; y++)
2094 for (x = 0; x < dst->w; x++)
2099 // advance source pointers
2103 // advance destination pointer
2107 // advance source pointer (for row)
2108 csp += ((*csay) * src->pitch);
2111 // advance destination pointers
2121 // ----------------------------------------------------------------------------
2124 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2125 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2126 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2127 // into a 32bit RGBA format on the fly.
2128 // ----------------------------------------------------------------------------
2130 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2132 SDL_Surface *zoom_src = NULL;
2133 SDL_Surface *zoom_dst = NULL;
2134 boolean is_converted = FALSE;
2141 // determine if source surface is 32 bit or 8 bit
2142 is_32bit = (src->format->BitsPerPixel == 32);
2144 if (is_32bit || src->format->BitsPerPixel == 8)
2146 // use source surface 'as is'
2151 // new source surface is 32 bit with a defined RGB ordering
2152 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2153 0x000000ff, 0x0000ff00, 0x00ff0000,
2154 (src->format->Amask ? 0xff000000 : 0));
2155 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2157 is_converted = TRUE;
2160 // allocate surface to completely contain the zoomed surface
2163 // target surface is 32 bit with source RGBA/ABGR ordering
2164 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2165 zoom_src->format->Rmask,
2166 zoom_src->format->Gmask,
2167 zoom_src->format->Bmask,
2168 zoom_src->format->Amask);
2172 // target surface is 8 bit
2173 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2177 // lock source surface
2178 SDL_LockSurface(zoom_src);
2180 // check which kind of surface we have
2183 // call the 32 bit transformation routine to do the zooming
2184 zoomSurfaceRGBA(zoom_src, zoom_dst);
2189 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2190 zoom_dst->format->palette->colors[i] =
2191 zoom_src->format->palette->colors[i];
2192 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2194 // call the 8 bit transformation routine to do the zooming
2195 zoomSurfaceY(zoom_src, zoom_dst);
2198 // unlock source surface
2199 SDL_UnlockSurface(zoom_src);
2201 // free temporary surface
2203 SDL_FreeSurface(zoom_src);
2205 // return destination surface
2209 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2211 SDL_Surface *new_surface;
2213 if (surface == NULL)
2216 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2217 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2219 // remove alpha channel from native non-transparent surface, if defined
2220 SDLSetAlpha(new_surface, FALSE, 0);
2222 // remove transparent color from native non-transparent surface, if defined
2223 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2228 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2230 Bitmap *dst_bitmap = CreateBitmapStruct();
2231 SDL_Surface *src_surface = src_bitmap->surface_masked;
2232 SDL_Surface *dst_surface;
2234 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2235 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2237 dst_bitmap->width = dst_width;
2238 dst_bitmap->height = dst_height;
2240 // create zoomed temporary surface from source surface
2241 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2243 // create native format destination surface from zoomed temporary surface
2244 SDLSetNativeSurface(&dst_surface);
2246 // set color key for zoomed surface from source surface, if defined
2247 if (SDLHasColorKey(src_surface))
2248 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2249 SDLGetColorKey(src_surface));
2251 // create native non-transparent surface for opaque blitting
2252 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2254 // set native transparent surface for masked blitting
2255 dst_bitmap->surface_masked = dst_surface;
2261 // ============================================================================
2262 // load image to bitmap
2263 // ============================================================================
2265 Bitmap *SDLLoadImage(char *filename)
2267 Bitmap *new_bitmap = CreateBitmapStruct();
2268 SDL_Surface *sdl_image_tmp;
2270 if (program.headless)
2272 // prevent sanity check warnings at later stage
2273 new_bitmap->width = new_bitmap->height = 1;
2278 print_timestamp_init("SDLLoadImage");
2280 print_timestamp_time(getBaseNamePtr(filename));
2282 // load image to temporary surface
2283 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2284 Error(ERR_EXIT, "IMG_Load('%s') failed: %s", getBaseNamePtr(filename),
2287 print_timestamp_time("IMG_Load");
2289 UPDATE_BUSY_STATE();
2291 // create native non-transparent surface for current image
2292 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2293 Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2295 print_timestamp_time("SDLGetNativeSurface (opaque)");
2297 UPDATE_BUSY_STATE();
2299 // set black pixel to transparent if no alpha channel / transparent color
2300 if (!SDLHasAlpha(sdl_image_tmp) &&
2301 !SDLHasColorKey(sdl_image_tmp))
2302 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2303 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2305 // create native transparent surface for current image
2306 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2307 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2309 print_timestamp_time("SDLGetNativeSurface (masked)");
2311 UPDATE_BUSY_STATE();
2313 // free temporary surface
2314 SDL_FreeSurface(sdl_image_tmp);
2316 new_bitmap->width = new_bitmap->surface->w;
2317 new_bitmap->height = new_bitmap->surface->h;
2319 print_timestamp_done("SDLLoadImage");
2325 // ----------------------------------------------------------------------------
2326 // custom cursor fuctions
2327 // ----------------------------------------------------------------------------
2329 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2331 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2332 cursor_info->width, cursor_info->height,
2333 cursor_info->hot_x, cursor_info->hot_y);
2336 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2338 static struct MouseCursorInfo *last_cursor_info = NULL;
2339 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2340 static SDL_Cursor *cursor_default = NULL;
2341 static SDL_Cursor *cursor_current = NULL;
2343 // if invoked for the first time, store the SDL default cursor
2344 if (cursor_default == NULL)
2345 cursor_default = SDL_GetCursor();
2347 // only create new cursor if cursor info (custom only) has changed
2348 if (cursor_info != NULL && cursor_info != last_cursor_info)
2350 cursor_current = create_cursor(cursor_info);
2351 last_cursor_info = cursor_info;
2354 // only set new cursor if cursor info (custom or NULL) has changed
2355 if (cursor_info != last_cursor_info2)
2356 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2358 last_cursor_info2 = cursor_info;
2362 // ============================================================================
2364 // ============================================================================
2366 void SDLOpenAudio(void)
2368 if (program.headless)
2371 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2373 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2377 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2378 AUDIO_NUM_CHANNELS_STEREO,
2379 setup.system.audio_fragment_size) < 0)
2381 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2385 audio.sound_available = TRUE;
2386 audio.music_available = TRUE;
2387 audio.loops_available = TRUE;
2388 audio.sound_enabled = TRUE;
2390 // set number of available mixer channels
2391 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2392 audio.music_channel = MUSIC_CHANNEL;
2393 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2395 Mixer_InitChannels();
2398 void SDLCloseAudio(void)
2401 Mix_HaltChannel(-1);
2404 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2408 // ============================================================================
2410 // ============================================================================
2412 void SDLWaitEvent(Event *event)
2414 SDL_WaitEvent(event);
2417 void SDLCorrectRawMousePosition(int *x, int *y)
2419 if (sdl_renderer == NULL)
2422 // this corrects the raw mouse position for logical screen size within event
2423 // filters (correction done later by SDL library when handling mouse events)
2426 float scale_x, scale_y;
2428 SDL_RenderGetViewport(sdl_renderer, &viewport);
2429 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2431 *x = (int)(*x / scale_x);
2432 *y = (int)(*y / scale_y);
2439 // ============================================================================
2440 // joystick functions
2441 // ============================================================================
2443 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2444 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2445 static int sdl_js_axis[MAX_PLAYERS][2];
2446 static int sdl_js_button[MAX_PLAYERS][2];
2447 static boolean sdl_is_controller[MAX_PLAYERS];
2449 void SDLClearJoystickState(void)
2453 for (i = 0; i < MAX_PLAYERS; i++)
2455 for (j = 0; j < 2; j++)
2457 sdl_js_axis_raw[i][j] = -1;
2458 sdl_js_axis[i][j] = 0;
2459 sdl_js_button[i][j] = 0;
2464 boolean SDLOpenJoystick(int nr)
2466 if (nr < 0 || nr >= MAX_PLAYERS)
2469 sdl_is_controller[nr] = SDL_IsGameController(nr);
2472 Error(ERR_DEBUG, "opening joystick %d (%s)",
2473 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2476 if (sdl_is_controller[nr])
2477 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2479 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2481 return (sdl_joystick[nr] != NULL);
2484 void SDLCloseJoystick(int nr)
2486 if (nr < 0 || nr >= MAX_PLAYERS)
2490 Error(ERR_DEBUG, "closing joystick %d", nr);
2493 if (sdl_is_controller[nr])
2494 SDL_GameControllerClose(sdl_joystick[nr]);
2496 SDL_JoystickClose(sdl_joystick[nr]);
2498 sdl_joystick[nr] = NULL;
2501 boolean SDLCheckJoystickOpened(int nr)
2503 if (nr < 0 || nr >= MAX_PLAYERS)
2506 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2509 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2511 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2512 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2513 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2514 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2516 if (nr < 0 || nr >= MAX_PLAYERS)
2522 // prevent (slightly jittering, but centered) axis A from resetting axis B
2523 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2524 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2527 sdl_js_axis[nr][axis_id] = axis_value;
2528 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2531 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2533 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2534 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2535 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2536 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2537 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2538 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2539 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2540 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2543 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2544 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2545 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2546 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2547 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2548 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2549 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2550 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2552 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2553 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2554 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2555 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2556 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2558 if (nr < 0 || nr >= MAX_PLAYERS)
2561 if (button_id == -1)
2564 sdl_js_button[nr][button_id] = button_state;
2567 void HandleJoystickEvent(Event *event)
2569 // when using joystick, disable overlay touch buttons
2570 runtime.uses_touch_device = FALSE;
2572 switch (event->type)
2574 case SDL_CONTROLLERDEVICEADDED:
2576 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2577 event->cdevice.which);
2582 case SDL_CONTROLLERDEVICEREMOVED:
2584 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2585 event->cdevice.which);
2590 case SDL_CONTROLLERAXISMOTION:
2592 Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2593 event->caxis.which, event->caxis.axis, event->caxis.value);
2595 setJoystickAxis(event->caxis.which,
2597 event->caxis.value);
2600 case SDL_CONTROLLERBUTTONDOWN:
2602 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2603 event->cbutton.which, event->cbutton.button);
2605 setJoystickButton(event->cbutton.which,
2606 event->cbutton.button,
2610 case SDL_CONTROLLERBUTTONUP:
2612 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2613 event->cbutton.which, event->cbutton.button);
2615 setJoystickButton(event->cbutton.which,
2616 event->cbutton.button,
2620 case SDL_JOYAXISMOTION:
2621 if (sdl_is_controller[event->jaxis.which])
2625 Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2626 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2628 if (event->jaxis.axis < 4)
2629 setJoystickAxis(event->jaxis.which,
2631 event->jaxis.value);
2634 case SDL_JOYBUTTONDOWN:
2635 if (sdl_is_controller[event->jaxis.which])
2639 Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2640 event->jbutton.which, event->jbutton.button);
2642 if (event->jbutton.button < 4)
2643 setJoystickButton(event->jbutton.which,
2644 event->jbutton.button,
2648 case SDL_JOYBUTTONUP:
2649 if (sdl_is_controller[event->jaxis.which])
2653 Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2654 event->jbutton.which, event->jbutton.button);
2656 if (event->jbutton.button < 4)
2657 setJoystickButton(event->jbutton.which,
2658 event->jbutton.button,
2667 void SDLInitJoysticks(void)
2669 static boolean sdl_joystick_subsystem_initialized = FALSE;
2670 boolean print_warning = !sdl_joystick_subsystem_initialized;
2671 char *mappings_file_base = getPath2(options.conf_directory,
2672 GAMECONTROLLER_BASENAME);
2673 char *mappings_file_user = getPath2(getUserGameDataDir(),
2674 GAMECONTROLLER_BASENAME);
2678 if (!sdl_joystick_subsystem_initialized)
2680 sdl_joystick_subsystem_initialized = TRUE;
2682 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2684 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2686 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2690 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2692 // the included game controller base mappings should always be found
2693 if (num_mappings == -1)
2694 Error(ERR_WARN, "no game controller base mappings found");
2697 Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2700 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2703 // the personal game controller user mappings may or may not be found
2704 if (num_mappings == -1)
2705 Error(ERR_WARN, "no game controller user mappings found");
2707 Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2709 Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2712 checked_free(mappings_file_base);
2713 checked_free(mappings_file_user);
2716 for (i = 0; i < SDL_NumJoysticks(); i++)
2718 const char *name, *type;
2720 if (SDL_IsGameController(i))
2722 name = SDL_GameControllerNameForIndex(i);
2723 type = "game controller";
2727 name = SDL_JoystickNameForIndex(i);
2731 Error(ERR_INFO, "- joystick %d (%s): '%s'",
2732 i, type, (name ? name : "(Unknown)"));
2737 // assign joysticks from configured to connected joystick for all players
2738 for (i = 0; i < MAX_PLAYERS; i++)
2740 // get configured joystick for this player
2741 char *device_name = setup.input[i].joy.device_name;
2742 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2744 if (joystick_nr >= SDL_NumJoysticks())
2746 if (setup.input[i].use_joystick && print_warning)
2747 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2752 // store configured joystick number for each player
2753 joystick.nr[i] = joystick_nr;
2756 // now open all connected joysticks (regardless if configured or not)
2757 for (i = 0; i < SDL_NumJoysticks(); i++)
2759 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2760 if (SDLCheckJoystickOpened(i))
2761 SDLCloseJoystick(i);
2763 if (SDLOpenJoystick(i))
2764 joystick.status = JOYSTICK_ACTIVATED;
2765 else if (print_warning)
2766 Error(ERR_WARN, "cannot open joystick %d", i);
2769 SDLClearJoystickState();
2772 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2774 if (nr < 0 || nr >= MAX_PLAYERS)
2778 *x = sdl_js_axis[nr][0];
2780 *y = sdl_js_axis[nr][1];
2783 *b1 = sdl_js_button[nr][0];
2785 *b2 = sdl_js_button[nr][1];
2791 // ============================================================================
2792 // touch input overlay functions
2793 // ============================================================================
2795 #if defined(USE_TOUCH_INPUT_OVERLAY)
2796 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2799 int grid_xsize = overlay.grid_xsize;
2800 int grid_ysize = overlay.grid_ysize;
2803 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2804 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2806 for (x = 0; x < grid_xsize; x++)
2808 rect.x = (x + 0) * video.screen_width / grid_xsize;
2809 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2811 for (y = 0; y < grid_ysize; y++)
2813 rect.y = (y + 0) * video.screen_height / grid_ysize;
2814 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2816 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2817 SDL_RenderDrawRect(sdl_renderer, &rect);
2821 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2824 static void RenderFillRectangle(int x, int y, int width, int height)
2826 SDL_Rect rect = { x, y, width, height };
2828 SDL_RenderFillRect(sdl_renderer, &rect);
2831 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2833 static int alpha_direction = 0;
2834 static int alpha_highlight = 0;
2835 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2836 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2838 int grid_xsize = overlay.grid_xsize;
2839 int grid_ysize = overlay.grid_ysize;
2842 if (alpha == alpha_max)
2844 if (alpha_direction < 0)
2846 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2848 if (alpha_highlight == 0)
2849 alpha_direction = 1;
2853 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2855 if (alpha_highlight == alpha_max)
2856 alpha_direction = -1;
2861 alpha_direction = 1;
2862 alpha_highlight = alpha;
2865 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2867 for (x = 0; x < grid_xsize; x++)
2869 for (y = 0; y < grid_ysize; y++)
2871 int grid_button = overlay.grid_button[x][y];
2872 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2873 int alpha_draw = alpha;
2874 int outline_border = MV_NONE;
2875 int border_size = 2;
2876 boolean draw_outlined = setup.touch.draw_outlined;
2877 boolean draw_pressed = setup.touch.draw_pressed;
2879 if (grid_button == CHAR_GRID_BUTTON_NONE)
2882 if (grid_button == overlay.grid_button_highlight)
2884 draw_outlined = FALSE;
2885 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2888 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2891 draw_outlined = FALSE;
2893 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2896 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2898 rect.x = (x + 0) * video.screen_width / grid_xsize;
2899 rect.y = (y + 0) * video.screen_height / grid_ysize;
2900 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2901 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2903 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2905 rect.x += border_size;
2906 rect.w -= border_size;
2908 outline_border |= MV_LEFT;
2911 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2913 rect.w -= border_size;
2915 outline_border |= MV_RIGHT;
2918 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2920 rect.y += border_size;
2921 rect.h -= border_size;
2923 outline_border |= MV_UP;
2926 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2928 rect.h -= border_size;
2930 outline_border |= MV_DOWN;
2935 int rect_x = rect.x +
2936 (outline_border & MV_LEFT ? border_size : 0);
2937 int rect_w = rect.w -
2938 (outline_border & MV_LEFT ? border_size : 0) -
2939 (outline_border & MV_RIGHT ? border_size : 0);
2941 if (outline_border & MV_LEFT)
2942 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2944 if (outline_border & MV_RIGHT)
2945 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2946 border_size, rect.h);
2948 if (outline_border & MV_UP)
2949 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2951 if (outline_border & MV_DOWN)
2952 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
2953 rect_w, border_size);
2957 SDL_RenderFillRect(sdl_renderer, &rect);
2962 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2965 static void DrawTouchInputOverlay(void)
2967 static boolean deactivated = TRUE;
2968 static boolean show_grid = FALSE;
2969 static int alpha = 0;
2970 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2971 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2972 boolean active = (overlay.enabled && overlay.active);
2974 if (!active && deactivated)
2979 if (alpha < alpha_max)
2980 alpha = MIN(alpha + alpha_step, alpha_max);
2982 deactivated = FALSE;
2986 alpha = MAX(0, alpha - alpha_step);
2992 if (overlay.show_grid)
2994 else if (deactivated)
2998 DrawTouchInputOverlay_ShowGrid(alpha);
3000 DrawTouchInputOverlay_ShowGridButtons(alpha);
3003 static void DrawTouchGadgetsOverlay(void)
3005 DrawGadgets_OverlayTouchButtons();