1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://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 Debug("internal:frame", "FrameCounter == %d [%s]", 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 Warn("SDLSetWindowIcon(): cannot find file '%s'", basename);
274 if ((surface = IMG_Load(filename)) == NULL)
276 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)
314 return (SDL_GetColorKey(surface, &color_key) == 0);
317 static boolean SDLHasAlpha(SDL_Surface *surface)
319 SDL_BlendMode blend_mode;
321 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
324 return (blend_mode == SDL_BLENDMODE_BLEND);
327 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
329 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
331 SDL_SetSurfaceBlendMode(surface, blend_mode);
332 SDL_SetSurfaceAlphaMod(surface, alpha);
335 const char *SDLGetRendererName(void)
337 static SDL_RendererInfo renderer_info;
339 SDL_GetRendererInfo(sdl_renderer, &renderer_info);
341 return renderer_info.name;
344 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
346 SDL_PixelFormat format;
347 SDL_Surface *new_surface;
352 if (backbuffer && backbuffer->surface)
354 format = *backbuffer->surface->format;
355 format.Amask = surface->format->Amask; // keep alpha channel
359 format = *surface->format;
362 new_surface = SDL_ConvertSurface(surface, &format, 0);
364 if (new_surface == NULL)
365 Fail("SDL_ConvertSurface() failed: %s", SDL_GetError());
367 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
368 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
369 SDL_SetColorKey(new_surface, SET_TRANSPARENT_PIXEL,
370 SDLGetColorKey(surface));
375 boolean SDLSetNativeSurface(SDL_Surface **surface)
377 SDL_Surface *new_surface;
379 if (surface == NULL ||
381 backbuffer == NULL ||
382 backbuffer->surface == NULL)
385 // if pixel format already optimized for destination surface, do nothing
386 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
389 new_surface = SDLGetNativeSurface(*surface);
391 SDL_FreeSurface(*surface);
393 *surface = new_surface;
398 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
400 if (program.headless)
403 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
406 Fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
411 void SDLCreateBitmapTextures(Bitmap *bitmap)
417 SDL_DestroyTexture(bitmap->texture);
418 if (bitmap->texture_masked)
419 SDL_DestroyTexture(bitmap->texture_masked);
421 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
422 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
425 void SDLFreeBitmapTextures(Bitmap *bitmap)
431 SDL_DestroyTexture(bitmap->texture);
432 if (bitmap->texture_masked)
433 SDL_DestroyTexture(bitmap->texture_masked);
435 bitmap->texture = NULL;
436 bitmap->texture_masked = NULL;
439 void SDLInitVideoDisplay(void)
441 // set hint to select render driver as specified in setup config file
442 if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
443 SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
445 // initialize SDL video
446 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
447 Fail("SDL_InitSubSystem() failed: %s", SDL_GetError());
449 // set default SDL depth
450 video.default_depth = 32; // (how to determine video depth in SDL2?)
452 // Code used with SDL 1.2:
453 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
456 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
458 if (program.headless)
461 video.window_scaling_percent = setup.window_scaling_percent;
462 video.window_scaling_quality = setup.window_scaling_quality;
464 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
466 // SDL 2.0: support for (desktop) fullscreen mode available
467 video.fullscreen_available = TRUE;
469 // open SDL video output device (window or fullscreen mode)
470 if (!SDLSetVideoMode(fullscreen))
471 Fail("setting video mode failed");
473 // !!! SDL2 can only set the window icon if the window already exists !!!
475 SDLSetWindowIcon(program.icon_filename);
477 // set window and icon title
481 static void SDLInitVideoBuffer_DrawBuffer(void)
483 /* SDL cannot directly draw to the visible video framebuffer like X11,
484 but always uses a backbuffer, which is then blitted to the visible
485 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
486 visible video framebuffer with 'SDL_Flip', if the hardware supports
487 this). Therefore do not use an additional backbuffer for drawing, but
488 use a symbolic buffer (distinguishable from the SDL backbuffer) called
489 'window', which indicates that the SDL backbuffer should be updated to
490 the visible video framebuffer when attempting to blit to it.
492 For convenience, it seems to be a good idea to create this symbolic
493 buffer 'window' at the same size as the SDL backbuffer. Although it
494 should never be drawn to directly, it would do no harm nevertheless. */
496 // create additional (symbolic) buffer for double-buffering
497 ReCreateBitmap(&window, video.width, video.height);
499 // create dummy drawing buffer for headless mode, if needed
500 if (program.headless)
501 ReCreateBitmap(&backbuffer, video.width, video.height);
504 void SDLInitVideoBuffer(boolean fullscreen)
506 SDLInitVideoBuffer_VideoBuffer(fullscreen);
507 SDLInitVideoBuffer_DrawBuffer();
510 static boolean SDLCreateScreen(boolean fullscreen)
512 SDL_Surface *new_surface = NULL;
514 int surface_flags_window = SURFACE_FLAGS;
515 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
518 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
520 video.vsync_mode = VSYNC_MODE_OFF;
522 if (!strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF))
524 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
525 video.vsync_mode = VSYNC_MODE_NORMAL;
528 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
529 _without_ enabling 2D/3D acceleration and/or guest additions installed,
530 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
531 it will try to use accelerated graphics and apparently fails miserably) */
532 int renderer_flags = SDL_RENDERER_SOFTWARE;
535 int width = video.width;
536 int height = video.height;
537 int screen_width = video.screen_width;
538 int screen_height = video.screen_height;
539 int surface_flags = (fullscreen ? surface_flags_fullscreen :
540 surface_flags_window);
542 // default window size is unscaled
543 video.window_width = screen_width;
544 video.window_height = screen_height;
546 // store if initial screen mode is fullscreen mode when changing screen size
547 video.fullscreen_initial = fullscreen;
549 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
551 video.window_width = window_scaling_factor * screen_width;
552 video.window_height = window_scaling_factor * screen_height;
554 if (sdl_texture_stream)
556 SDL_DestroyTexture(sdl_texture_stream);
557 sdl_texture_stream = NULL;
560 if (sdl_texture_target)
562 SDL_DestroyTexture(sdl_texture_target);
563 sdl_texture_target = NULL;
566 if (!(fullscreen && fullscreen_enabled))
570 SDL_DestroyRenderer(sdl_renderer);
576 SDL_DestroyWindow(sdl_window);
581 if (sdl_window == NULL)
582 sdl_window = SDL_CreateWindow(program.window_title,
583 SDL_WINDOWPOS_CENTERED,
584 SDL_WINDOWPOS_CENTERED,
589 if (sdl_window != NULL)
591 if (sdl_renderer == NULL)
592 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
594 if (sdl_renderer != NULL)
596 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
597 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
599 // required for setting adaptive vsync when using OpenGL renderer
600 SDLSetScreenVsyncMode(setup.vsync_mode);
602 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
603 SDL_PIXELFORMAT_ARGB8888,
604 SDL_TEXTUREACCESS_STREAMING,
607 if (SDL_RenderTargetSupported(sdl_renderer))
608 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
609 SDL_PIXELFORMAT_ARGB8888,
610 SDL_TEXTUREACCESS_TARGET,
613 if (sdl_texture_stream != NULL)
615 // use SDL default values for RGB masks and no alpha channel
616 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
618 if (new_surface == NULL)
619 Warn("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
623 Warn("SDL_CreateTexture() failed: %s", SDL_GetError());
628 Warn("SDL_CreateRenderer() failed: %s", SDL_GetError());
633 Warn("SDL_CreateWindow() failed: %s", SDL_GetError());
636 SDLSetScreenProperties();
638 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
639 if (new_surface != NULL)
640 fullscreen_enabled = fullscreen;
642 if (backbuffer == NULL)
643 backbuffer = CreateBitmapStruct();
645 backbuffer->width = video.width;
646 backbuffer->height = video.height;
648 if (backbuffer->surface)
649 SDL_FreeSurface(backbuffer->surface);
651 backbuffer->surface = new_surface;
653 return (new_surface != NULL);
656 boolean SDLSetVideoMode(boolean fullscreen)
658 boolean success = FALSE;
662 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
664 // switch display to fullscreen mode, if available
665 success = SDLCreateScreen(TRUE);
669 // switching display to fullscreen mode failed -- do not try it again
670 video.fullscreen_available = FALSE;
674 video.fullscreen_enabled = TRUE;
678 if ((!fullscreen && video.fullscreen_enabled) || !success)
680 // switch display to window mode
681 success = SDLCreateScreen(FALSE);
685 // switching display to window mode failed -- should not happen
689 video.fullscreen_enabled = FALSE;
690 video.window_scaling_percent = setup.window_scaling_percent;
691 video.window_scaling_quality = setup.window_scaling_quality;
693 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
697 SDLRedrawWindow(); // map window
702 void SDLSetWindowTitle(void)
704 if (sdl_window == NULL)
707 SDL_SetWindowTitle(sdl_window, program.window_title);
710 void SDLSetWindowScaling(int window_scaling_percent)
712 if (sdl_window == NULL)
715 float window_scaling_factor = (float)window_scaling_percent / 100;
716 int new_window_width = (int)(window_scaling_factor * video.screen_width);
717 int new_window_height = (int)(window_scaling_factor * video.screen_height);
719 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
721 video.window_scaling_percent = window_scaling_percent;
722 video.window_width = new_window_width;
723 video.window_height = new_window_height;
728 void SDLSetWindowScalingQuality(char *window_scaling_quality)
730 SDL_Texture *new_texture;
732 if (sdl_texture_stream == NULL)
735 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
737 new_texture = SDL_CreateTexture(sdl_renderer,
738 SDL_PIXELFORMAT_ARGB8888,
739 SDL_TEXTUREACCESS_STREAMING,
740 video.width, video.height);
742 if (new_texture != NULL)
744 SDL_DestroyTexture(sdl_texture_stream);
746 sdl_texture_stream = new_texture;
749 if (SDL_RenderTargetSupported(sdl_renderer))
750 new_texture = SDL_CreateTexture(sdl_renderer,
751 SDL_PIXELFORMAT_ARGB8888,
752 SDL_TEXTUREACCESS_TARGET,
753 video.width, video.height);
757 if (new_texture != NULL)
759 SDL_DestroyTexture(sdl_texture_target);
761 sdl_texture_target = new_texture;
766 video.window_scaling_quality = window_scaling_quality;
769 void SDLSetWindowFullscreen(boolean fullscreen)
771 if (sdl_window == NULL)
774 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
776 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
777 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
779 // if screen size was changed in fullscreen mode, correct desktop window size
780 if (!fullscreen && video.fullscreen_initial)
782 SDLSetWindowScaling(setup.window_scaling_percent);
783 SDL_SetWindowPosition(sdl_window,
784 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
786 video.fullscreen_initial = FALSE;
790 void SDLSetDisplaySize(void)
792 if (sdl_renderer != NULL)
796 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
798 video.display_width = w;
799 video.display_height = h;
802 Debug("video", "SDL renderer size: %d x %d",
803 video.display_width, video.display_height);
808 SDL_Rect display_bounds;
810 SDL_GetDisplayBounds(0, &display_bounds);
812 video.display_width = display_bounds.w;
813 video.display_height = display_bounds.h;
816 Debug("video", "SDL display size: %d x %d",
817 video.display_width, video.display_height);
822 void SDLSetScreenSizeAndOffsets(int width, int height)
824 // set default video screen size and offsets
825 video.screen_width = width;
826 video.screen_height = height;
827 video.screen_xoffset = 0;
828 video.screen_yoffset = 0;
830 #if defined(USE_COMPLETE_DISPLAY)
831 float ratio_video = (float) width / height;
832 float ratio_display = (float) video.display_width / video.display_height;
834 if (ratio_video != ratio_display)
836 // adjust drawable screen size to cover the whole device display
838 if (ratio_video < ratio_display)
839 video.screen_width *= ratio_display / ratio_video;
841 video.screen_height *= ratio_video / ratio_display;
843 video.screen_xoffset = (video.screen_width - width) / 2;
844 video.screen_yoffset = (video.screen_height - height) / 2;
847 Debug("video", "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
849 video.screen_width, video.screen_height,
850 ratio_video, ratio_display);
856 void SDLSetScreenSizeForRenderer(int width, int height)
858 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
861 void SDLSetScreenProperties(void)
864 SDLSetScreenSizeAndOffsets(video.width, video.height);
865 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
868 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
870 video.screen_rendering_mode =
871 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
872 SPECIAL_RENDERING_BITMAP :
873 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
874 SPECIAL_RENDERING_TARGET:
875 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
876 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
879 void SDLSetScreenVsyncMode(char *vsync_mode)
881 // changing vsync mode without re-creating renderer only supported by OpenGL
882 if (!strPrefixLower((char *)SDLGetRendererName(), "opengl"))
885 int interval = VSYNC_MODE_STR_TO_INT(vsync_mode);
886 int result = SDL_GL_SetSwapInterval(interval);
888 // if adaptive vsync requested, but not supported, retry with normal vsync
889 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
891 interval = VSYNC_MODE_NORMAL;
893 result = SDL_GL_SetSwapInterval(interval);
897 interval = VSYNC_MODE_OFF;
899 video.vsync_mode = interval;
902 void SDLRedrawWindow(void)
904 UpdateScreen_WithoutFrameDelay(NULL);
907 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
910 if (program.headless)
913 SDL_Surface *surface =
914 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
917 Fail("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
919 SDLSetNativeSurface(&surface);
921 bitmap->surface = surface;
924 void SDLFreeBitmapPointers(Bitmap *bitmap)
927 SDL_FreeSurface(bitmap->surface);
928 if (bitmap->surface_masked)
929 SDL_FreeSurface(bitmap->surface_masked);
931 bitmap->surface = NULL;
932 bitmap->surface_masked = NULL;
935 SDL_DestroyTexture(bitmap->texture);
936 if (bitmap->texture_masked)
937 SDL_DestroyTexture(bitmap->texture_masked);
939 bitmap->texture = NULL;
940 bitmap->texture_masked = NULL;
943 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
944 int src_x, int src_y, int width, int height,
945 int dst_x, int dst_y, int mask_mode)
947 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
948 SDL_Rect src_rect, dst_rect;
960 // if (src_bitmap != backbuffer || dst_bitmap != window)
961 if (!(src_bitmap == backbuffer && dst_bitmap == window))
962 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
963 src_bitmap->surface_masked : src_bitmap->surface),
964 &src_rect, real_dst_bitmap->surface, &dst_rect);
966 if (dst_bitmap == window)
967 UpdateScreen_WithFrameDelay(&dst_rect);
970 void SDLBlitTexture(Bitmap *bitmap,
971 int src_x, int src_y, int width, int height,
972 int dst_x, int dst_y, int mask_mode)
974 SDL_Texture *texture;
979 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
994 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
997 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1000 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1008 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1010 if (dst_bitmap == window)
1011 UpdateScreen_WithFrameDelay(&rect);
1014 void PrepareFadeBitmap(int draw_target)
1016 Bitmap *fade_bitmap =
1017 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1018 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1020 if (fade_bitmap == NULL)
1023 // copy backbuffer to fading buffer
1024 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1026 // add border and animations to fading buffer
1027 FinalizeScreen(draw_target);
1030 void SDLFadeRectangle(int x, int y, int width, int height,
1031 int fade_mode, int fade_delay, int post_delay,
1032 void (*draw_border_function)(void))
1034 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1035 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1036 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1037 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1038 SDL_Surface *surface_screen = backbuffer->surface;
1039 SDL_Rect src_rect, dst_rect;
1041 int src_x = x, src_y = y;
1042 int dst_x = x, dst_y = y;
1043 unsigned int time_last, time_current;
1045 // store function for drawing global masked border
1046 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1048 // deactivate drawing of global border while fading, if needed
1049 if (draw_border_function == NULL)
1050 gfx.draw_global_border_function = NULL;
1055 src_rect.h = height;
1059 dst_rect.w = width; // (ignored)
1060 dst_rect.h = height; // (ignored)
1062 dst_rect2 = dst_rect;
1064 // before fading in, store backbuffer (without animation graphics)
1065 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1066 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1068 // copy source and target surfaces to temporary surfaces for fading
1069 if (fade_mode & FADE_TYPE_TRANSFORM)
1071 // (source and target fading buffer already prepared)
1073 else if (fade_mode & FADE_TYPE_FADE_IN)
1075 // (target fading buffer already prepared)
1076 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1078 else // FADE_TYPE_FADE_OUT
1080 // (source fading buffer already prepared)
1081 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1084 time_current = SDL_GetTicks();
1086 if (fade_delay <= 0)
1088 // immediately draw final target frame without delay
1089 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1093 // when fading without delay, also skip post delay
1097 if (fade_mode == FADE_MODE_MELT)
1099 boolean done = FALSE;
1100 int melt_pixels = 2;
1101 int melt_columns = width / melt_pixels;
1102 int ypos[melt_columns];
1103 int max_steps = height / 8 + 32;
1108 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1110 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1112 ypos[0] = -GetSimpleRandom(16);
1114 for (i = 1 ; i < melt_columns; i++)
1116 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1118 ypos[i] = ypos[i - 1] + r;
1131 time_last = time_current;
1132 time_current = SDL_GetTicks();
1133 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1134 steps_final = MIN(MAX(0, steps), max_steps);
1138 done = (steps_done >= steps_final);
1140 for (i = 0 ; i < melt_columns; i++)
1148 else if (ypos[i] < height)
1153 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1155 if (ypos[i] + dy >= height)
1156 dy = height - ypos[i];
1158 // copy part of (appearing) target surface to upper area
1159 src_rect.x = src_x + i * melt_pixels;
1160 // src_rect.y = src_y + ypos[i];
1162 src_rect.w = melt_pixels;
1164 src_rect.h = ypos[i] + dy;
1166 dst_rect.x = dst_x + i * melt_pixels;
1167 // dst_rect.y = dst_y + ypos[i];
1170 if (steps_done >= steps_final)
1171 SDL_BlitSurface(surface_target, &src_rect,
1172 surface_screen, &dst_rect);
1176 // copy part of (disappearing) source surface to lower area
1177 src_rect.x = src_x + i * melt_pixels;
1179 src_rect.w = melt_pixels;
1180 src_rect.h = height - ypos[i];
1182 dst_rect.x = dst_x + i * melt_pixels;
1183 dst_rect.y = dst_y + ypos[i];
1185 if (steps_done >= steps_final)
1186 SDL_BlitSurface(surface_source, &src_rect,
1187 surface_screen, &dst_rect);
1193 src_rect.x = src_x + i * melt_pixels;
1195 src_rect.w = melt_pixels;
1196 src_rect.h = height;
1198 dst_rect.x = dst_x + i * melt_pixels;
1201 if (steps_done >= steps_final)
1202 SDL_BlitSurface(surface_target, &src_rect,
1203 surface_screen, &dst_rect);
1207 if (steps_done >= steps_final)
1209 if (draw_border_function != NULL)
1210 draw_border_function();
1212 UpdateScreen_WithFrameDelay(&dst_rect2);
1216 else if (fade_mode == FADE_MODE_CURTAIN)
1220 int xx_size = width / 2;
1222 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1224 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1226 for (xx = 0; xx < xx_size;)
1228 time_last = time_current;
1229 time_current = SDL_GetTicks();
1230 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1231 xx_final = MIN(MAX(0, xx), xx_size);
1236 src_rect.h = height;
1241 // draw new (target) image to screen buffer
1242 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1244 if (xx_final < xx_size)
1246 src_rect.w = xx_size - xx_final;
1247 src_rect.h = height;
1249 // draw old (source) image to screen buffer (left side)
1251 src_rect.x = src_x + xx_final;
1254 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1256 // draw old (source) image to screen buffer (right side)
1258 src_rect.x = src_x + xx_size;
1259 dst_rect.x = dst_x + xx_size + xx_final;
1261 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1264 if (draw_border_function != NULL)
1265 draw_border_function();
1267 // only update the region of the screen that is affected from fading
1268 UpdateScreen_WithFrameDelay(&dst_rect2);
1271 else // fading in, fading out or cross-fading
1276 for (alpha = 0.0; alpha < 255.0;)
1278 time_last = time_current;
1279 time_current = SDL_GetTicks();
1280 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1281 alpha_final = MIN(MAX(0, alpha), 255);
1283 // draw existing (source) image to screen buffer
1284 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1286 // draw new (target) image to screen buffer using alpha blending
1287 SDLSetAlpha(surface_target, TRUE, alpha_final);
1288 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1290 if (draw_border_function != NULL)
1291 draw_border_function();
1293 // only update the region of the screen that is affected from fading
1294 UpdateScreen_WithFrameDelay(&dst_rect);
1299 Delay_WithScreenUpdates(post_delay);
1301 // restore function for drawing global masked border
1302 gfx.draw_global_border_function = draw_global_border_function;
1304 // after fading in, restore backbuffer (without animation graphics)
1305 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1306 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1309 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1310 int to_x, int to_y, Uint32 color)
1312 SDL_Surface *surface = dst_bitmap->surface;
1316 swap_numbers(&from_x, &to_x);
1319 swap_numbers(&from_y, &to_y);
1323 rect.w = (to_x - from_x + 1);
1324 rect.h = (to_y - from_y + 1);
1326 SDL_FillRect(surface, &rect, color);
1329 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1330 int to_x, int to_y, Uint32 color)
1332 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1335 #if ENABLE_UNUSED_CODE
1336 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1337 int num_points, Uint32 color)
1342 for (i = 0; i < num_points - 1; i++)
1344 for (x = 0; x < line_width; x++)
1346 for (y = 0; y < line_width; y++)
1348 int dx = x - line_width / 2;
1349 int dy = y - line_width / 2;
1351 if ((x == 0 && y == 0) ||
1352 (x == 0 && y == line_width - 1) ||
1353 (x == line_width - 1 && y == 0) ||
1354 (x == line_width - 1 && y == line_width - 1))
1357 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1358 points[i+1].x + dx, points[i+1].y + dy, color);
1365 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1367 SDL_Surface *surface = src_bitmap->surface;
1369 switch (surface->format->BytesPerPixel)
1371 case 1: // assuming 8-bpp
1373 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1377 case 2: // probably 15-bpp or 16-bpp
1379 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1383 case 3: // slow 24-bpp mode; usually not used
1386 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1390 shift = surface->format->Rshift;
1391 color |= *(pix + shift / 8) >> shift;
1392 shift = surface->format->Gshift;
1393 color |= *(pix + shift / 8) >> shift;
1394 shift = surface->format->Bshift;
1395 color |= *(pix + shift / 8) >> shift;
1401 case 4: // probably 32-bpp
1403 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1412 // ============================================================================
1413 // The following functions were taken from the SGE library
1414 // (SDL Graphics Extension Library) by Anders Lindström
1415 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1416 // ============================================================================
1418 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1420 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1422 switch (surface->format->BytesPerPixel)
1427 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1433 // Probably 15-bpp or 16-bpp
1434 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1440 // Slow 24-bpp mode, usually not used
1444 // Gack - slow, but endian correct
1445 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1446 shift = surface->format->Rshift;
1447 *(pix+shift/8) = color>>shift;
1448 shift = surface->format->Gshift;
1449 *(pix+shift/8) = color>>shift;
1450 shift = surface->format->Bshift;
1451 *(pix+shift/8) = color>>shift;
1458 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1466 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1467 Uint8 R, Uint8 G, Uint8 B)
1469 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1472 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1474 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1477 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1479 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1482 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1487 // Gack - slow, but endian correct
1488 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1489 shift = surface->format->Rshift;
1490 *(pix+shift/8) = color>>shift;
1491 shift = surface->format->Gshift;
1492 *(pix+shift/8) = color>>shift;
1493 shift = surface->format->Bshift;
1494 *(pix+shift/8) = color>>shift;
1497 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1499 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1502 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1504 switch (dest->format->BytesPerPixel)
1507 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1511 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1515 _PutPixel24(dest,x,y,color);
1519 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1525 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1527 if (SDL_MUSTLOCK(surface))
1529 if (SDL_LockSurface(surface) < 0)
1535 _PutPixel(surface, x, y, color);
1537 if (SDL_MUSTLOCK(surface))
1539 SDL_UnlockSurface(surface);
1544 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1545 Uint8 r, Uint8 g, Uint8 b)
1547 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1550 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1552 if (y >= 0 && y <= dest->h - 1)
1554 switch (dest->format->BytesPerPixel)
1557 return y*dest->pitch;
1561 return y*dest->pitch/2;
1565 return y*dest->pitch;
1569 return y*dest->pitch/4;
1577 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1580 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1582 switch (surface->format->BytesPerPixel)
1587 *((Uint8 *)surface->pixels + ypitch + x) = color;
1593 // Probably 15-bpp or 16-bpp
1594 *((Uint16 *)surface->pixels + ypitch + x) = color;
1600 // Slow 24-bpp mode, usually not used
1604 // Gack - slow, but endian correct
1605 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1606 shift = surface->format->Rshift;
1607 *(pix+shift/8) = color>>shift;
1608 shift = surface->format->Gshift;
1609 *(pix+shift/8) = color>>shift;
1610 shift = surface->format->Bshift;
1611 *(pix+shift/8) = color>>shift;
1618 *((Uint32 *)surface->pixels + ypitch + x) = color;
1625 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1630 if (SDL_MUSTLOCK(Surface))
1632 if (SDL_LockSurface(Surface) < 0)
1646 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1650 if (x2 > Surface->w - 1)
1651 x2 = Surface->w - 1;
1658 SDL_FillRect(Surface, &l, Color);
1660 if (SDL_MUSTLOCK(Surface))
1662 SDL_UnlockSurface(Surface);
1666 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1667 Uint8 R, Uint8 G, Uint8 B)
1669 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1672 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1685 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1689 if (x2 > Surface->w - 1)
1690 x2 = Surface->w - 1;
1697 SDL_FillRect(Surface, &l, Color);
1700 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1705 if (SDL_MUSTLOCK(Surface))
1707 if (SDL_LockSurface(Surface) < 0)
1721 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1725 if (y2 > Surface->h - 1)
1726 y2 = Surface->h - 1;
1733 SDL_FillRect(Surface, &l, Color);
1735 if (SDL_MUSTLOCK(Surface))
1737 SDL_UnlockSurface(Surface);
1741 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1742 Uint8 R, Uint8 G, Uint8 B)
1744 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1747 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1760 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1764 if (y2 > Surface->h - 1)
1765 y2 = Surface->h - 1;
1772 SDL_FillRect(Surface, &l, Color);
1776 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1777 Sint16 x2, Sint16 y2, Uint32 Color,
1778 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1781 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1786 sdx = (dx < 0) ? -1 : 1;
1787 sdy = (dy < 0) ? -1 : 1;
1799 for (x = 0; x < dx; x++)
1801 Callback(Surface, px, py, Color);
1815 for (y = 0; y < dy; y++)
1817 Callback(Surface, px, py, Color);
1832 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1833 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1834 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1837 sge_DoLine(Surface, X1, Y1, X2, Y2,
1838 SDL_MapRGB(Surface->format, R, G, B), Callback);
1842 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1845 if (SDL_MUSTLOCK(Surface))
1847 if (SDL_LockSurface(Surface) < 0)
1852 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1854 // unlock the display
1855 if (SDL_MUSTLOCK(Surface))
1857 SDL_UnlockSurface(Surface);
1862 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1863 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1865 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1869 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1871 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1875 // ----------------------------------------------------------------------------
1876 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1877 // ----------------------------------------------------------------------------
1879 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1880 int width, int height, Uint32 color)
1884 for (y = src_y; y < src_y + height; y++)
1886 for (x = src_x; x < src_x + width; x++)
1888 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1890 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1895 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1896 int src_x, int src_y, int width, int height,
1897 int dst_x, int dst_y)
1901 for (y = 0; y < height; y++)
1903 for (x = 0; x < width; x++)
1905 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1907 if (pixel != BLACK_PIXEL)
1908 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1914 // ============================================================================
1915 // The following functions were taken from the SDL_gfx library version 2.0.3
1916 // (Rotozoomer) by Andreas Schiffler
1917 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1918 // ============================================================================
1920 // ----------------------------------------------------------------------------
1923 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1924 // ----------------------------------------------------------------------------
1934 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1937 tColorRGBA *sp, *csp, *dp;
1941 sp = csp = (tColorRGBA *) src->pixels;
1942 dp = (tColorRGBA *) dst->pixels;
1943 dgap = dst->pitch - dst->w * 4;
1945 for (y = 0; y < dst->h; y++)
1949 for (x = 0; x < dst->w; x++)
1951 tColorRGBA *sp0 = sp;
1952 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1953 tColorRGBA *sp00 = &sp0[0];
1954 tColorRGBA *sp01 = &sp0[1];
1955 tColorRGBA *sp10 = &sp1[0];
1956 tColorRGBA *sp11 = &sp1[1];
1959 // create new color pixel from all four source color pixels
1960 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1961 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1962 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1963 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1968 // advance source pointers
1971 // advance destination pointer
1975 // advance source pointer
1976 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1978 // advance destination pointers
1979 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1985 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1987 int x, y, *sax, *say, *csax, *csay;
1989 tColorRGBA *sp, *csp, *csp0, *dp;
1992 // use specialized zoom function when scaling down to exactly half size
1993 if (src->w == 2 * dst->w &&
1994 src->h == 2 * dst->h)
1995 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1998 sx = (float) src->w / (float) dst->w;
1999 sy = (float) src->h / (float) dst->h;
2001 // allocate memory for row increments
2002 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2003 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2005 // precalculate row increments
2006 for (x = 0; x <= dst->w; x++)
2007 *csax++ = (int)(sx * x);
2009 for (y = 0; y <= dst->h; y++)
2010 *csay++ = (int)(sy * y);
2013 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2014 dp = (tColorRGBA *) dst->pixels;
2015 dgap = dst->pitch - dst->w * 4;
2018 for (y = 0; y < dst->h; y++)
2023 for (x = 0; x < dst->w; x++)
2028 // advance source pointers
2032 // advance destination pointer
2036 // advance source pointer
2038 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2040 // advance destination pointers
2041 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2050 // ----------------------------------------------------------------------------
2053 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2054 // ----------------------------------------------------------------------------
2056 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2058 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2059 Uint8 *sp, *dp, *csp;
2063 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2064 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2066 // allocate memory for row increments
2067 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2068 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2070 // precalculate row increments
2073 for (x = 0; x < dst->w; x++)
2076 *csax = (csx >> 16);
2083 for (y = 0; y < dst->h; y++)
2086 *csay = (csy >> 16);
2093 for (x = 0; x < dst->w; x++)
2101 for (y = 0; y < dst->h; y++)
2108 sp = csp = (Uint8 *) src->pixels;
2109 dp = (Uint8 *) dst->pixels;
2110 dgap = dst->pitch - dst->w;
2114 for (y = 0; y < dst->h; y++)
2118 for (x = 0; x < dst->w; x++)
2123 // advance source pointers
2127 // advance destination pointer
2131 // advance source pointer (for row)
2132 csp += ((*csay) * src->pitch);
2135 // advance destination pointers
2145 // ----------------------------------------------------------------------------
2148 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2149 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2150 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2151 // into a 32bit RGBA format on the fly.
2152 // ----------------------------------------------------------------------------
2154 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2156 SDL_Surface *zoom_src = NULL;
2157 SDL_Surface *zoom_dst = NULL;
2158 boolean is_converted = FALSE;
2165 // determine if source surface is 32 bit or 8 bit
2166 is_32bit = (src->format->BitsPerPixel == 32);
2168 if (is_32bit || src->format->BitsPerPixel == 8)
2170 // use source surface 'as is'
2175 // new source surface is 32 bit with a defined RGB ordering
2176 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2177 0x000000ff, 0x0000ff00, 0x00ff0000,
2178 (src->format->Amask ? 0xff000000 : 0));
2179 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2181 is_converted = TRUE;
2184 // allocate surface to completely contain the zoomed surface
2187 // target surface is 32 bit with source RGBA/ABGR ordering
2188 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2189 zoom_src->format->Rmask,
2190 zoom_src->format->Gmask,
2191 zoom_src->format->Bmask,
2192 zoom_src->format->Amask);
2196 // target surface is 8 bit
2197 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2201 // lock source surface
2202 SDL_LockSurface(zoom_src);
2204 // check which kind of surface we have
2207 // call the 32 bit transformation routine to do the zooming
2208 zoomSurfaceRGBA(zoom_src, zoom_dst);
2213 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2214 zoom_dst->format->palette->colors[i] =
2215 zoom_src->format->palette->colors[i];
2216 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2218 // call the 8 bit transformation routine to do the zooming
2219 zoomSurfaceY(zoom_src, zoom_dst);
2222 // unlock source surface
2223 SDL_UnlockSurface(zoom_src);
2225 // free temporary surface
2227 SDL_FreeSurface(zoom_src);
2229 // return destination surface
2233 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2235 SDL_Surface *new_surface;
2237 if (surface == NULL)
2240 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2241 Fail("SDLGetNativeSurface() failed");
2243 // remove alpha channel from native non-transparent surface, if defined
2244 SDLSetAlpha(new_surface, FALSE, 0);
2246 // remove transparent color from native non-transparent surface, if defined
2247 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2252 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2254 Bitmap *dst_bitmap = CreateBitmapStruct();
2255 SDL_Surface *src_surface = src_bitmap->surface_masked;
2256 SDL_Surface *dst_surface;
2258 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2259 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2261 dst_bitmap->width = dst_width;
2262 dst_bitmap->height = dst_height;
2264 // create zoomed temporary surface from source surface
2265 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2267 // create native format destination surface from zoomed temporary surface
2268 SDLSetNativeSurface(&dst_surface);
2270 // set color key for zoomed surface from source surface, if defined
2271 if (SDLHasColorKey(src_surface))
2272 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2273 SDLGetColorKey(src_surface));
2275 // create native non-transparent surface for opaque blitting
2276 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2278 // set native transparent surface for masked blitting
2279 dst_bitmap->surface_masked = dst_surface;
2285 // ============================================================================
2286 // load image to bitmap
2287 // ============================================================================
2289 Bitmap *SDLLoadImage(char *filename)
2291 Bitmap *new_bitmap = CreateBitmapStruct();
2292 SDL_Surface *sdl_image_tmp;
2294 if (program.headless)
2296 // prevent sanity check warnings at later stage
2297 new_bitmap->width = new_bitmap->height = 1;
2302 print_timestamp_init("SDLLoadImage");
2304 print_timestamp_time(getBaseNamePtr(filename));
2306 // load image to temporary surface
2307 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2308 Fail("IMG_Load('%s') failed: %s", getBaseNamePtr(filename), SDL_GetError());
2310 print_timestamp_time("IMG_Load");
2312 UPDATE_BUSY_STATE();
2314 // create native non-transparent surface for current image
2315 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2316 Fail("SDLGetOpaqueSurface() failed");
2318 print_timestamp_time("SDLGetNativeSurface (opaque)");
2320 UPDATE_BUSY_STATE();
2322 // set black pixel to transparent if no alpha channel / transparent color
2323 if (!SDLHasAlpha(sdl_image_tmp) &&
2324 !SDLHasColorKey(sdl_image_tmp))
2325 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2326 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2328 // create native transparent surface for current image
2329 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2330 Fail("SDLGetNativeSurface() failed");
2332 print_timestamp_time("SDLGetNativeSurface (masked)");
2334 UPDATE_BUSY_STATE();
2336 // free temporary surface
2337 SDL_FreeSurface(sdl_image_tmp);
2339 new_bitmap->width = new_bitmap->surface->w;
2340 new_bitmap->height = new_bitmap->surface->h;
2342 print_timestamp_done("SDLLoadImage");
2348 // ----------------------------------------------------------------------------
2349 // custom cursor fuctions
2350 // ----------------------------------------------------------------------------
2352 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2354 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2355 cursor_info->width, cursor_info->height,
2356 cursor_info->hot_x, cursor_info->hot_y);
2359 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2361 static struct MouseCursorInfo *last_cursor_info = NULL;
2362 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2363 static SDL_Cursor *cursor_default = NULL;
2364 static SDL_Cursor *cursor_current = NULL;
2366 // if invoked for the first time, store the SDL default cursor
2367 if (cursor_default == NULL)
2368 cursor_default = SDL_GetCursor();
2370 // only create new cursor if cursor info (custom only) has changed
2371 if (cursor_info != NULL && cursor_info != last_cursor_info)
2373 cursor_current = create_cursor(cursor_info);
2374 last_cursor_info = cursor_info;
2377 // only set new cursor if cursor info (custom or NULL) has changed
2378 if (cursor_info != last_cursor_info2)
2379 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2381 last_cursor_info2 = cursor_info;
2385 // ============================================================================
2387 // ============================================================================
2389 void SDLOpenAudio(void)
2391 if (program.headless)
2394 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2396 Warn("SDL_InitSubSystem() failed: %s", SDL_GetError());
2401 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2402 AUDIO_NUM_CHANNELS_STEREO,
2403 setup.system.audio_fragment_size) < 0)
2405 Warn("Mix_OpenAudio() failed: %s", SDL_GetError());
2410 audio.sound_available = TRUE;
2411 audio.music_available = TRUE;
2412 audio.loops_available = TRUE;
2413 audio.sound_enabled = TRUE;
2415 // set number of available mixer channels
2416 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2417 audio.music_channel = MUSIC_CHANNEL;
2418 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2420 Mixer_InitChannels();
2423 void SDLCloseAudio(void)
2426 Mix_HaltChannel(-1);
2429 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2433 // ============================================================================
2435 // ============================================================================
2437 void SDLWaitEvent(Event *event)
2439 SDL_WaitEvent(event);
2442 void SDLCorrectRawMousePosition(int *x, int *y)
2444 if (sdl_renderer == NULL)
2447 // this corrects the raw mouse position for logical screen size within event
2448 // filters (correction done later by SDL library when handling mouse events)
2451 float scale_x, scale_y;
2453 SDL_RenderGetViewport(sdl_renderer, &viewport);
2454 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2456 *x = (int)(*x / scale_x);
2457 *y = (int)(*y / scale_y);
2464 // ============================================================================
2465 // joystick functions
2466 // ============================================================================
2468 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2469 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2470 static int sdl_js_axis[MAX_PLAYERS][2];
2471 static int sdl_js_button[MAX_PLAYERS][2];
2472 static boolean sdl_is_controller[MAX_PLAYERS];
2474 void SDLClearJoystickState(void)
2478 for (i = 0; i < MAX_PLAYERS; i++)
2480 for (j = 0; j < 2; j++)
2482 sdl_js_axis_raw[i][j] = -1;
2483 sdl_js_axis[i][j] = 0;
2484 sdl_js_button[i][j] = 0;
2489 boolean SDLOpenJoystick(int nr)
2491 if (nr < 0 || nr >= MAX_PLAYERS)
2494 sdl_is_controller[nr] = SDL_IsGameController(nr);
2497 Debug("joystick", "opening joystick %d (%s)",
2498 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2501 if (sdl_is_controller[nr])
2502 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2504 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2506 return (sdl_joystick[nr] != NULL);
2509 void SDLCloseJoystick(int nr)
2511 if (nr < 0 || nr >= MAX_PLAYERS)
2515 Debug("joystick", "closing joystick %d", nr);
2518 if (sdl_is_controller[nr])
2519 SDL_GameControllerClose(sdl_joystick[nr]);
2521 SDL_JoystickClose(sdl_joystick[nr]);
2523 sdl_joystick[nr] = NULL;
2526 boolean SDLCheckJoystickOpened(int nr)
2528 if (nr < 0 || nr >= MAX_PLAYERS)
2531 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2534 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2536 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2537 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2538 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2539 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2541 if (nr < 0 || nr >= MAX_PLAYERS)
2547 // prevent (slightly jittering, but centered) axis A from resetting axis B
2548 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2549 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2552 sdl_js_axis[nr][axis_id] = axis_value;
2553 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2556 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2558 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2559 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2560 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2561 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2562 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2563 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2564 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2565 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2568 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2569 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2570 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2571 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2572 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2573 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2574 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2575 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2577 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2578 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2579 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2580 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2581 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2583 if (nr < 0 || nr >= MAX_PLAYERS)
2586 if (button_id == -1)
2589 sdl_js_button[nr][button_id] = button_state;
2592 void HandleJoystickEvent(Event *event)
2594 // when using joystick, disable overlay touch buttons
2595 runtime.uses_touch_device = FALSE;
2597 switch (event->type)
2599 case SDL_CONTROLLERDEVICEADDED:
2601 Debug("joystick", "SDL_CONTROLLERDEVICEADDED: device %d added",
2602 event->cdevice.which);
2607 case SDL_CONTROLLERDEVICEREMOVED:
2609 Debug("joystick", "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2610 event->cdevice.which);
2615 case SDL_CONTROLLERAXISMOTION:
2617 Debug("joystick", "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2618 event->caxis.which, event->caxis.axis, event->caxis.value);
2620 setJoystickAxis(event->caxis.which,
2622 event->caxis.value);
2625 case SDL_CONTROLLERBUTTONDOWN:
2627 Debug("joystick", "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2628 event->cbutton.which, event->cbutton.button);
2630 setJoystickButton(event->cbutton.which,
2631 event->cbutton.button,
2635 case SDL_CONTROLLERBUTTONUP:
2637 Debug("joystick", "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2638 event->cbutton.which, event->cbutton.button);
2640 setJoystickButton(event->cbutton.which,
2641 event->cbutton.button,
2645 case SDL_JOYAXISMOTION:
2646 if (sdl_is_controller[event->jaxis.which])
2650 Debug("joystick", "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2651 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2653 if (event->jaxis.axis < 4)
2654 setJoystickAxis(event->jaxis.which,
2656 event->jaxis.value);
2659 case SDL_JOYBUTTONDOWN:
2660 if (sdl_is_controller[event->jaxis.which])
2664 Debug("joystick", "SDL_JOYBUTTONDOWN: device %d, button %d",
2665 event->jbutton.which, event->jbutton.button);
2667 if (event->jbutton.button < 4)
2668 setJoystickButton(event->jbutton.which,
2669 event->jbutton.button,
2673 case SDL_JOYBUTTONUP:
2674 if (sdl_is_controller[event->jaxis.which])
2678 Debug("joystick", "SDL_JOYBUTTONUP: device %d, button %d",
2679 event->jbutton.which, event->jbutton.button);
2681 if (event->jbutton.button < 4)
2682 setJoystickButton(event->jbutton.which,
2683 event->jbutton.button,
2692 void SDLInitJoysticks(void)
2694 static boolean sdl_joystick_subsystem_initialized = FALSE;
2695 boolean print_warning = !sdl_joystick_subsystem_initialized;
2696 char *mappings_file_base = getPath2(options.conf_directory,
2697 GAMECONTROLLER_BASENAME);
2698 char *mappings_file_user = getPath2(getUserGameDataDir(),
2699 GAMECONTROLLER_BASENAME);
2703 if (!sdl_joystick_subsystem_initialized)
2705 sdl_joystick_subsystem_initialized = TRUE;
2707 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2709 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2710 Fail("SDL_Init() failed: %s", SDL_GetError());
2712 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2714 // the included game controller base mappings should always be found
2715 if (num_mappings == -1)
2716 Warn("no game controller base mappings found");
2719 Debug("joystick", "%d game controller base mapping(s) added",
2723 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2726 // the personal game controller user mappings may or may not be found
2727 if (num_mappings == -1)
2728 Warn("no game controller user mappings found");
2730 Debug("joystick", , "%d game controller user mapping(s) added",
2733 Debug("joystick", "%d joystick(s) found:", SDL_NumJoysticks());
2736 checked_free(mappings_file_base);
2737 checked_free(mappings_file_user);
2740 for (i = 0; i < SDL_NumJoysticks(); i++)
2742 const char *name, *type;
2744 if (SDL_IsGameController(i))
2746 name = SDL_GameControllerNameForIndex(i);
2747 type = "game controller";
2751 name = SDL_JoystickNameForIndex(i);
2755 Debug("joystick", "- joystick %d (%s): '%s'",
2756 i, type, (name ? name : "(Unknown)"));
2761 // assign joysticks from configured to connected joystick for all players
2762 for (i = 0; i < MAX_PLAYERS; i++)
2764 // get configured joystick for this player
2765 char *device_name = setup.input[i].joy.device_name;
2766 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2768 if (joystick_nr >= SDL_NumJoysticks())
2770 if (setup.input[i].use_joystick && print_warning)
2771 Warn("cannot find joystick %d", joystick_nr);
2776 // store configured joystick number for each player
2777 joystick.nr[i] = joystick_nr;
2780 // now open all connected joysticks (regardless if configured or not)
2781 for (i = 0; i < SDL_NumJoysticks(); i++)
2783 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2784 if (SDLCheckJoystickOpened(i))
2785 SDLCloseJoystick(i);
2787 if (SDLOpenJoystick(i))
2788 joystick.status = JOYSTICK_ACTIVATED;
2789 else if (print_warning)
2790 Warn("cannot open joystick %d", i);
2793 SDLClearJoystickState();
2796 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2798 if (nr < 0 || nr >= MAX_PLAYERS)
2802 *x = sdl_js_axis[nr][0];
2804 *y = sdl_js_axis[nr][1];
2807 *b1 = sdl_js_button[nr][0];
2809 *b2 = sdl_js_button[nr][1];
2815 // ============================================================================
2816 // touch input overlay functions
2817 // ============================================================================
2819 #if defined(USE_TOUCH_INPUT_OVERLAY)
2820 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2823 int grid_xsize = overlay.grid_xsize;
2824 int grid_ysize = overlay.grid_ysize;
2827 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2828 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2830 for (x = 0; x < grid_xsize; x++)
2832 rect.x = (x + 0) * video.screen_width / grid_xsize;
2833 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2835 for (y = 0; y < grid_ysize; y++)
2837 rect.y = (y + 0) * video.screen_height / grid_ysize;
2838 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2840 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2841 SDL_RenderDrawRect(sdl_renderer, &rect);
2845 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2848 static void RenderFillRectangle(int x, int y, int width, int height)
2850 SDL_Rect rect = { x, y, width, height };
2852 SDL_RenderFillRect(sdl_renderer, &rect);
2855 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2857 static int alpha_direction = 0;
2858 static int alpha_highlight = 0;
2859 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2860 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2862 int grid_xsize = overlay.grid_xsize;
2863 int grid_ysize = overlay.grid_ysize;
2866 if (alpha == alpha_max)
2868 if (alpha_direction < 0)
2870 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2872 if (alpha_highlight == 0)
2873 alpha_direction = 1;
2877 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2879 if (alpha_highlight == alpha_max)
2880 alpha_direction = -1;
2885 alpha_direction = 1;
2886 alpha_highlight = alpha;
2889 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2891 for (x = 0; x < grid_xsize; x++)
2893 for (y = 0; y < grid_ysize; y++)
2895 int grid_button = overlay.grid_button[x][y];
2896 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2897 int alpha_draw = alpha;
2898 int outline_border = MV_NONE;
2899 int border_size = 2;
2900 boolean draw_outlined = setup.touch.draw_outlined;
2901 boolean draw_pressed = setup.touch.draw_pressed;
2903 if (grid_button == CHAR_GRID_BUTTON_NONE)
2906 if (grid_button == overlay.grid_button_highlight)
2908 draw_outlined = FALSE;
2909 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2912 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2915 draw_outlined = FALSE;
2917 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2920 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2922 rect.x = (x + 0) * video.screen_width / grid_xsize;
2923 rect.y = (y + 0) * video.screen_height / grid_ysize;
2924 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2925 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2927 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2929 rect.x += border_size;
2930 rect.w -= border_size;
2932 outline_border |= MV_LEFT;
2935 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2937 rect.w -= border_size;
2939 outline_border |= MV_RIGHT;
2942 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2944 rect.y += border_size;
2945 rect.h -= border_size;
2947 outline_border |= MV_UP;
2950 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2952 rect.h -= border_size;
2954 outline_border |= MV_DOWN;
2959 int rect_x = rect.x +
2960 (outline_border & MV_LEFT ? border_size : 0);
2961 int rect_w = rect.w -
2962 (outline_border & MV_LEFT ? border_size : 0) -
2963 (outline_border & MV_RIGHT ? border_size : 0);
2965 if (outline_border & MV_LEFT)
2966 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2968 if (outline_border & MV_RIGHT)
2969 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2970 border_size, rect.h);
2972 if (outline_border & MV_UP)
2973 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2975 if (outline_border & MV_DOWN)
2976 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
2977 rect_w, border_size);
2981 SDL_RenderFillRect(sdl_renderer, &rect);
2986 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2989 static void DrawTouchInputOverlay(void)
2991 static boolean deactivated = TRUE;
2992 static boolean show_grid = FALSE;
2993 static int alpha = 0;
2994 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2995 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2996 boolean active = (overlay.enabled && overlay.active);
2998 if (!active && deactivated)
3003 if (alpha < alpha_max)
3004 alpha = MIN(alpha + alpha_step, alpha_max);
3006 deactivated = FALSE;
3010 alpha = MAX(0, alpha - alpha_step);
3016 if (overlay.show_grid)
3018 else if (deactivated)
3022 DrawTouchInputOverlay_ShowGrid(alpha);
3024 DrawTouchInputOverlay_ShowGridButtons(alpha);
3027 static void DrawTouchGadgetsOverlay(void)
3029 DrawGadgets_OverlayTouchButtons();