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 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 // set hint to select render driver as specified in setup config file
441 if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
442 SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
444 // initialize SDL video
445 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
446 Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
448 // set default SDL depth
449 video.default_depth = 32; // (how to determine video depth in SDL2?)
451 // Code used with SDL 1.2:
452 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
455 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
457 if (program.headless)
460 video.window_scaling_percent = setup.window_scaling_percent;
461 video.window_scaling_quality = setup.window_scaling_quality;
463 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
465 // SDL 2.0: support for (desktop) fullscreen mode available
466 video.fullscreen_available = TRUE;
468 // open SDL video output device (window or fullscreen mode)
469 if (!SDLSetVideoMode(fullscreen))
470 Error(ERR_EXIT, "setting video mode failed");
472 // !!! SDL2 can only set the window icon if the window already exists !!!
474 SDLSetWindowIcon(program.icon_filename);
476 // set window and icon title
480 static void SDLInitVideoBuffer_DrawBuffer(void)
482 /* SDL cannot directly draw to the visible video framebuffer like X11,
483 but always uses a backbuffer, which is then blitted to the visible
484 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
485 visible video framebuffer with 'SDL_Flip', if the hardware supports
486 this). Therefore do not use an additional backbuffer for drawing, but
487 use a symbolic buffer (distinguishable from the SDL backbuffer) called
488 'window', which indicates that the SDL backbuffer should be updated to
489 the visible video framebuffer when attempting to blit to it.
491 For convenience, it seems to be a good idea to create this symbolic
492 buffer 'window' at the same size as the SDL backbuffer. Although it
493 should never be drawn to directly, it would do no harm nevertheless. */
495 // create additional (symbolic) buffer for double-buffering
496 ReCreateBitmap(&window, video.width, video.height);
498 // create dummy drawing buffer for headless mode, if needed
499 if (program.headless)
500 ReCreateBitmap(&backbuffer, video.width, video.height);
503 void SDLInitVideoBuffer(boolean fullscreen)
505 SDLInitVideoBuffer_VideoBuffer(fullscreen);
506 SDLInitVideoBuffer_DrawBuffer();
509 static boolean SDLCreateScreen(boolean fullscreen)
511 SDL_Surface *new_surface = NULL;
513 int surface_flags_window = SURFACE_FLAGS;
514 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
517 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
519 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
520 _without_ enabling 2D/3D acceleration and/or guest additions installed,
521 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
522 it will try to use accelerated graphics and apparently fails miserably) */
523 int renderer_flags = SDL_RENDERER_SOFTWARE;
526 int width = video.width;
527 int height = video.height;
528 int screen_width = video.screen_width;
529 int screen_height = video.screen_height;
530 int surface_flags = (fullscreen ? surface_flags_fullscreen :
531 surface_flags_window);
533 // default window size is unscaled
534 video.window_width = screen_width;
535 video.window_height = screen_height;
537 // store if initial screen mode is fullscreen mode when changing screen size
538 video.fullscreen_initial = fullscreen;
540 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
542 video.window_width = window_scaling_factor * screen_width;
543 video.window_height = window_scaling_factor * screen_height;
545 if (sdl_texture_stream)
547 SDL_DestroyTexture(sdl_texture_stream);
548 sdl_texture_stream = NULL;
551 if (sdl_texture_target)
553 SDL_DestroyTexture(sdl_texture_target);
554 sdl_texture_target = NULL;
557 if (!(fullscreen && fullscreen_enabled))
561 SDL_DestroyRenderer(sdl_renderer);
567 SDL_DestroyWindow(sdl_window);
572 if (sdl_window == NULL)
573 sdl_window = SDL_CreateWindow(program.window_title,
574 SDL_WINDOWPOS_CENTERED,
575 SDL_WINDOWPOS_CENTERED,
580 if (sdl_window != NULL)
582 if (sdl_renderer == NULL)
583 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
585 if (sdl_renderer != NULL)
587 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
588 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
590 SDLSetScreenVsyncMode(setup.vsync_mode);
592 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
593 SDL_PIXELFORMAT_ARGB8888,
594 SDL_TEXTUREACCESS_STREAMING,
597 if (SDL_RenderTargetSupported(sdl_renderer))
598 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
599 SDL_PIXELFORMAT_ARGB8888,
600 SDL_TEXTUREACCESS_TARGET,
603 if (sdl_texture_stream != NULL)
605 // use SDL default values for RGB masks and no alpha channel
606 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
608 if (new_surface == NULL)
609 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
613 Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
618 Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
623 Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
626 SDLSetScreenProperties();
628 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
629 if (new_surface != NULL)
630 fullscreen_enabled = fullscreen;
632 if (backbuffer == NULL)
633 backbuffer = CreateBitmapStruct();
635 backbuffer->width = video.width;
636 backbuffer->height = video.height;
638 if (backbuffer->surface)
639 SDL_FreeSurface(backbuffer->surface);
641 backbuffer->surface = new_surface;
643 return (new_surface != NULL);
646 boolean SDLSetVideoMode(boolean fullscreen)
648 boolean success = FALSE;
652 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
654 // switch display to fullscreen mode, if available
655 success = SDLCreateScreen(TRUE);
659 // switching display to fullscreen mode failed -- do not try it again
660 video.fullscreen_available = FALSE;
664 video.fullscreen_enabled = TRUE;
668 if ((!fullscreen && video.fullscreen_enabled) || !success)
670 // switch display to window mode
671 success = SDLCreateScreen(FALSE);
675 // switching display to window mode failed -- should not happen
679 video.fullscreen_enabled = FALSE;
680 video.window_scaling_percent = setup.window_scaling_percent;
681 video.window_scaling_quality = setup.window_scaling_quality;
683 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
687 SDLRedrawWindow(); // map window
692 void SDLSetWindowTitle(void)
694 if (sdl_window == NULL)
697 SDL_SetWindowTitle(sdl_window, program.window_title);
700 void SDLSetWindowScaling(int window_scaling_percent)
702 if (sdl_window == NULL)
705 float window_scaling_factor = (float)window_scaling_percent / 100;
706 int new_window_width = (int)(window_scaling_factor * video.screen_width);
707 int new_window_height = (int)(window_scaling_factor * video.screen_height);
709 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
711 video.window_scaling_percent = window_scaling_percent;
712 video.window_width = new_window_width;
713 video.window_height = new_window_height;
718 void SDLSetWindowScalingQuality(char *window_scaling_quality)
720 SDL_Texture *new_texture;
722 if (sdl_texture_stream == NULL)
725 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
727 new_texture = SDL_CreateTexture(sdl_renderer,
728 SDL_PIXELFORMAT_ARGB8888,
729 SDL_TEXTUREACCESS_STREAMING,
730 video.width, video.height);
732 if (new_texture != NULL)
734 SDL_DestroyTexture(sdl_texture_stream);
736 sdl_texture_stream = new_texture;
739 if (SDL_RenderTargetSupported(sdl_renderer))
740 new_texture = SDL_CreateTexture(sdl_renderer,
741 SDL_PIXELFORMAT_ARGB8888,
742 SDL_TEXTUREACCESS_TARGET,
743 video.width, video.height);
747 if (new_texture != NULL)
749 SDL_DestroyTexture(sdl_texture_target);
751 sdl_texture_target = new_texture;
756 video.window_scaling_quality = window_scaling_quality;
759 void SDLSetWindowFullscreen(boolean fullscreen)
761 if (sdl_window == NULL)
764 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
766 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
767 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
769 // if screen size was changed in fullscreen mode, correct desktop window size
770 if (!fullscreen && video.fullscreen_initial)
772 SDLSetWindowScaling(setup.window_scaling_percent);
773 SDL_SetWindowPosition(sdl_window,
774 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
776 video.fullscreen_initial = FALSE;
780 void SDLSetDisplaySize(void)
782 if (sdl_renderer != NULL)
786 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
788 video.display_width = w;
789 video.display_height = h;
792 Error(ERR_DEBUG, "SDL renderer size: %d x %d",
793 video.display_width, video.display_height);
798 SDL_Rect display_bounds;
800 SDL_GetDisplayBounds(0, &display_bounds);
802 video.display_width = display_bounds.w;
803 video.display_height = display_bounds.h;
806 Error(ERR_DEBUG, "SDL display size: %d x %d",
807 video.display_width, video.display_height);
812 void SDLSetScreenSizeAndOffsets(int width, int height)
814 // set default video screen size and offsets
815 video.screen_width = width;
816 video.screen_height = height;
817 video.screen_xoffset = 0;
818 video.screen_yoffset = 0;
820 #if defined(USE_COMPLETE_DISPLAY)
821 float ratio_video = (float) width / height;
822 float ratio_display = (float) video.display_width / video.display_height;
824 if (ratio_video != ratio_display)
826 // adjust drawable screen size to cover the whole device display
828 if (ratio_video < ratio_display)
829 video.screen_width *= ratio_display / ratio_video;
831 video.screen_height *= ratio_video / ratio_display;
833 video.screen_xoffset = (video.screen_width - width) / 2;
834 video.screen_yoffset = (video.screen_height - height) / 2;
837 Error(ERR_DEBUG, "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
839 video.screen_width, video.screen_height,
840 ratio_video, ratio_display);
846 void SDLSetScreenSizeForRenderer(int width, int height)
848 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
851 void SDLSetScreenProperties(void)
854 SDLSetScreenSizeAndOffsets(video.width, video.height);
855 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
858 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
860 video.screen_rendering_mode =
861 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
862 SPECIAL_RENDERING_BITMAP :
863 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
864 SPECIAL_RENDERING_TARGET:
865 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
866 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
869 void SDLSetScreenVsyncMode(char *vsync_mode)
872 (strEqual(vsync_mode, STR_VSYNC_MODE_NORMAL) ? VSYNC_MODE_NORMAL :
873 strEqual(vsync_mode, STR_VSYNC_MODE_ADAPTIVE) ? VSYNC_MODE_ADAPTIVE :
875 int result = SDL_GL_SetSwapInterval(interval);
877 // if adaptive vsync requested, but not supported, retry with normal vsync
878 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
879 SDL_GL_SetSwapInterval(VSYNC_MODE_NORMAL);
882 void SDLRedrawWindow(void)
884 UpdateScreen_WithoutFrameDelay(NULL);
887 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
890 if (program.headless)
893 SDL_Surface *surface =
894 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
897 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
899 SDLSetNativeSurface(&surface);
901 bitmap->surface = surface;
904 void SDLFreeBitmapPointers(Bitmap *bitmap)
907 SDL_FreeSurface(bitmap->surface);
908 if (bitmap->surface_masked)
909 SDL_FreeSurface(bitmap->surface_masked);
911 bitmap->surface = NULL;
912 bitmap->surface_masked = NULL;
915 SDL_DestroyTexture(bitmap->texture);
916 if (bitmap->texture_masked)
917 SDL_DestroyTexture(bitmap->texture_masked);
919 bitmap->texture = NULL;
920 bitmap->texture_masked = NULL;
923 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
924 int src_x, int src_y, int width, int height,
925 int dst_x, int dst_y, int mask_mode)
927 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
928 SDL_Rect src_rect, dst_rect;
940 // if (src_bitmap != backbuffer || dst_bitmap != window)
941 if (!(src_bitmap == backbuffer && dst_bitmap == window))
942 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
943 src_bitmap->surface_masked : src_bitmap->surface),
944 &src_rect, real_dst_bitmap->surface, &dst_rect);
946 if (dst_bitmap == window)
947 UpdateScreen_WithFrameDelay(&dst_rect);
950 void SDLBlitTexture(Bitmap *bitmap,
951 int src_x, int src_y, int width, int height,
952 int dst_x, int dst_y, int mask_mode)
954 SDL_Texture *texture;
959 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
974 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
977 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
980 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
988 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
990 if (dst_bitmap == window)
991 UpdateScreen_WithFrameDelay(&rect);
994 void PrepareFadeBitmap(int draw_target)
996 Bitmap *fade_bitmap =
997 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
998 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1000 if (fade_bitmap == NULL)
1003 // copy backbuffer to fading buffer
1004 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1006 // add border and animations to fading buffer
1007 FinalizeScreen(draw_target);
1010 void SDLFadeRectangle(int x, int y, int width, int height,
1011 int fade_mode, int fade_delay, int post_delay,
1012 void (*draw_border_function)(void))
1014 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1015 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1016 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1017 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1018 SDL_Surface *surface_screen = backbuffer->surface;
1019 SDL_Rect src_rect, dst_rect;
1021 int src_x = x, src_y = y;
1022 int dst_x = x, dst_y = y;
1023 unsigned int time_last, time_current;
1025 // store function for drawing global masked border
1026 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1028 // deactivate drawing of global border while fading, if needed
1029 if (draw_border_function == NULL)
1030 gfx.draw_global_border_function = NULL;
1035 src_rect.h = height;
1039 dst_rect.w = width; // (ignored)
1040 dst_rect.h = height; // (ignored)
1042 dst_rect2 = dst_rect;
1044 // before fading in, store backbuffer (without animation graphics)
1045 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1046 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1048 // copy source and target surfaces to temporary surfaces for fading
1049 if (fade_mode & FADE_TYPE_TRANSFORM)
1051 // (source and target fading buffer already prepared)
1053 else if (fade_mode & FADE_TYPE_FADE_IN)
1055 // (target fading buffer already prepared)
1056 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1058 else // FADE_TYPE_FADE_OUT
1060 // (source fading buffer already prepared)
1061 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1064 time_current = SDL_GetTicks();
1066 if (fade_delay <= 0)
1068 // immediately draw final target frame without delay
1069 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1073 // when fading without delay, also skip post delay
1077 if (fade_mode == FADE_MODE_MELT)
1079 boolean done = FALSE;
1080 int melt_pixels = 2;
1081 int melt_columns = width / melt_pixels;
1082 int ypos[melt_columns];
1083 int max_steps = height / 8 + 32;
1088 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1090 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1092 ypos[0] = -GetSimpleRandom(16);
1094 for (i = 1 ; i < melt_columns; i++)
1096 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1098 ypos[i] = ypos[i - 1] + r;
1111 time_last = time_current;
1112 time_current = SDL_GetTicks();
1113 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1114 steps_final = MIN(MAX(0, steps), max_steps);
1118 done = (steps_done >= steps_final);
1120 for (i = 0 ; i < melt_columns; i++)
1128 else if (ypos[i] < height)
1133 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1135 if (ypos[i] + dy >= height)
1136 dy = height - ypos[i];
1138 // copy part of (appearing) target surface to upper area
1139 src_rect.x = src_x + i * melt_pixels;
1140 // src_rect.y = src_y + ypos[i];
1142 src_rect.w = melt_pixels;
1144 src_rect.h = ypos[i] + dy;
1146 dst_rect.x = dst_x + i * melt_pixels;
1147 // dst_rect.y = dst_y + ypos[i];
1150 if (steps_done >= steps_final)
1151 SDL_BlitSurface(surface_target, &src_rect,
1152 surface_screen, &dst_rect);
1156 // copy part of (disappearing) source surface to lower area
1157 src_rect.x = src_x + i * melt_pixels;
1159 src_rect.w = melt_pixels;
1160 src_rect.h = height - ypos[i];
1162 dst_rect.x = dst_x + i * melt_pixels;
1163 dst_rect.y = dst_y + ypos[i];
1165 if (steps_done >= steps_final)
1166 SDL_BlitSurface(surface_source, &src_rect,
1167 surface_screen, &dst_rect);
1173 src_rect.x = src_x + i * melt_pixels;
1175 src_rect.w = melt_pixels;
1176 src_rect.h = height;
1178 dst_rect.x = dst_x + i * melt_pixels;
1181 if (steps_done >= steps_final)
1182 SDL_BlitSurface(surface_target, &src_rect,
1183 surface_screen, &dst_rect);
1187 if (steps_done >= steps_final)
1189 if (draw_border_function != NULL)
1190 draw_border_function();
1192 UpdateScreen_WithFrameDelay(&dst_rect2);
1196 else if (fade_mode == FADE_MODE_CURTAIN)
1200 int xx_size = width / 2;
1202 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1204 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1206 for (xx = 0; xx < xx_size;)
1208 time_last = time_current;
1209 time_current = SDL_GetTicks();
1210 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1211 xx_final = MIN(MAX(0, xx), xx_size);
1216 src_rect.h = height;
1221 // draw new (target) image to screen buffer
1222 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1224 if (xx_final < xx_size)
1226 src_rect.w = xx_size - xx_final;
1227 src_rect.h = height;
1229 // draw old (source) image to screen buffer (left side)
1231 src_rect.x = src_x + xx_final;
1234 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1236 // draw old (source) image to screen buffer (right side)
1238 src_rect.x = src_x + xx_size;
1239 dst_rect.x = dst_x + xx_size + xx_final;
1241 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1244 if (draw_border_function != NULL)
1245 draw_border_function();
1247 // only update the region of the screen that is affected from fading
1248 UpdateScreen_WithFrameDelay(&dst_rect2);
1251 else // fading in, fading out or cross-fading
1256 for (alpha = 0.0; alpha < 255.0;)
1258 time_last = time_current;
1259 time_current = SDL_GetTicks();
1260 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1261 alpha_final = MIN(MAX(0, alpha), 255);
1263 // draw existing (source) image to screen buffer
1264 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1266 // draw new (target) image to screen buffer using alpha blending
1267 SDLSetAlpha(surface_target, TRUE, alpha_final);
1268 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1270 if (draw_border_function != NULL)
1271 draw_border_function();
1273 // only update the region of the screen that is affected from fading
1274 UpdateScreen_WithFrameDelay(&dst_rect);
1279 Delay_WithScreenUpdates(post_delay);
1281 // restore function for drawing global masked border
1282 gfx.draw_global_border_function = draw_global_border_function;
1284 // after fading in, restore backbuffer (without animation graphics)
1285 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1286 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1289 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1290 int to_x, int to_y, Uint32 color)
1292 SDL_Surface *surface = dst_bitmap->surface;
1296 swap_numbers(&from_x, &to_x);
1299 swap_numbers(&from_y, &to_y);
1303 rect.w = (to_x - from_x + 1);
1304 rect.h = (to_y - from_y + 1);
1306 SDL_FillRect(surface, &rect, color);
1309 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1310 int to_x, int to_y, Uint32 color)
1312 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1315 #if ENABLE_UNUSED_CODE
1316 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1317 int num_points, Uint32 color)
1322 for (i = 0; i < num_points - 1; i++)
1324 for (x = 0; x < line_width; x++)
1326 for (y = 0; y < line_width; y++)
1328 int dx = x - line_width / 2;
1329 int dy = y - line_width / 2;
1331 if ((x == 0 && y == 0) ||
1332 (x == 0 && y == line_width - 1) ||
1333 (x == line_width - 1 && y == 0) ||
1334 (x == line_width - 1 && y == line_width - 1))
1337 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1338 points[i+1].x + dx, points[i+1].y + dy, color);
1345 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1347 SDL_Surface *surface = src_bitmap->surface;
1349 switch (surface->format->BytesPerPixel)
1351 case 1: // assuming 8-bpp
1353 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1357 case 2: // probably 15-bpp or 16-bpp
1359 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1363 case 3: // slow 24-bpp mode; usually not used
1366 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1370 shift = surface->format->Rshift;
1371 color |= *(pix + shift / 8) >> shift;
1372 shift = surface->format->Gshift;
1373 color |= *(pix + shift / 8) >> shift;
1374 shift = surface->format->Bshift;
1375 color |= *(pix + shift / 8) >> shift;
1381 case 4: // probably 32-bpp
1383 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1392 // ============================================================================
1393 // The following functions were taken from the SGE library
1394 // (SDL Graphics Extension Library) by Anders Lindström
1395 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1396 // ============================================================================
1398 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1400 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1402 switch (surface->format->BytesPerPixel)
1407 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1413 // Probably 15-bpp or 16-bpp
1414 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1420 // Slow 24-bpp mode, usually not used
1424 // Gack - slow, but endian correct
1425 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1426 shift = surface->format->Rshift;
1427 *(pix+shift/8) = color>>shift;
1428 shift = surface->format->Gshift;
1429 *(pix+shift/8) = color>>shift;
1430 shift = surface->format->Bshift;
1431 *(pix+shift/8) = color>>shift;
1438 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1446 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1447 Uint8 R, Uint8 G, Uint8 B)
1449 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1452 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1454 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1457 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1459 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1462 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1467 // Gack - slow, but endian correct
1468 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1469 shift = surface->format->Rshift;
1470 *(pix+shift/8) = color>>shift;
1471 shift = surface->format->Gshift;
1472 *(pix+shift/8) = color>>shift;
1473 shift = surface->format->Bshift;
1474 *(pix+shift/8) = color>>shift;
1477 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1479 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1482 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1484 switch (dest->format->BytesPerPixel)
1487 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1491 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1495 _PutPixel24(dest,x,y,color);
1499 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1505 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1507 if (SDL_MUSTLOCK(surface))
1509 if (SDL_LockSurface(surface) < 0)
1515 _PutPixel(surface, x, y, color);
1517 if (SDL_MUSTLOCK(surface))
1519 SDL_UnlockSurface(surface);
1524 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1525 Uint8 r, Uint8 g, Uint8 b)
1527 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1530 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1532 if (y >= 0 && y <= dest->h - 1)
1534 switch (dest->format->BytesPerPixel)
1537 return y*dest->pitch;
1541 return y*dest->pitch/2;
1545 return y*dest->pitch;
1549 return y*dest->pitch/4;
1557 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1560 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1562 switch (surface->format->BytesPerPixel)
1567 *((Uint8 *)surface->pixels + ypitch + x) = color;
1573 // Probably 15-bpp or 16-bpp
1574 *((Uint16 *)surface->pixels + ypitch + x) = color;
1580 // Slow 24-bpp mode, usually not used
1584 // Gack - slow, but endian correct
1585 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1586 shift = surface->format->Rshift;
1587 *(pix+shift/8) = color>>shift;
1588 shift = surface->format->Gshift;
1589 *(pix+shift/8) = color>>shift;
1590 shift = surface->format->Bshift;
1591 *(pix+shift/8) = color>>shift;
1598 *((Uint32 *)surface->pixels + ypitch + x) = color;
1605 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1610 if (SDL_MUSTLOCK(Surface))
1612 if (SDL_LockSurface(Surface) < 0)
1626 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1630 if (x2 > Surface->w - 1)
1631 x2 = Surface->w - 1;
1638 SDL_FillRect(Surface, &l, Color);
1640 if (SDL_MUSTLOCK(Surface))
1642 SDL_UnlockSurface(Surface);
1646 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1647 Uint8 R, Uint8 G, Uint8 B)
1649 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1652 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1665 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1669 if (x2 > Surface->w - 1)
1670 x2 = Surface->w - 1;
1677 SDL_FillRect(Surface, &l, Color);
1680 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1685 if (SDL_MUSTLOCK(Surface))
1687 if (SDL_LockSurface(Surface) < 0)
1701 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1705 if (y2 > Surface->h - 1)
1706 y2 = Surface->h - 1;
1713 SDL_FillRect(Surface, &l, Color);
1715 if (SDL_MUSTLOCK(Surface))
1717 SDL_UnlockSurface(Surface);
1721 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1722 Uint8 R, Uint8 G, Uint8 B)
1724 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1727 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1740 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1744 if (y2 > Surface->h - 1)
1745 y2 = Surface->h - 1;
1752 SDL_FillRect(Surface, &l, Color);
1756 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1757 Sint16 x2, Sint16 y2, Uint32 Color,
1758 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1761 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1766 sdx = (dx < 0) ? -1 : 1;
1767 sdy = (dy < 0) ? -1 : 1;
1779 for (x = 0; x < dx; x++)
1781 Callback(Surface, px, py, Color);
1795 for (y = 0; y < dy; y++)
1797 Callback(Surface, px, py, Color);
1812 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1813 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1814 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1817 sge_DoLine(Surface, X1, Y1, X2, Y2,
1818 SDL_MapRGB(Surface->format, R, G, B), Callback);
1822 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1825 if (SDL_MUSTLOCK(Surface))
1827 if (SDL_LockSurface(Surface) < 0)
1832 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1834 // unlock the display
1835 if (SDL_MUSTLOCK(Surface))
1837 SDL_UnlockSurface(Surface);
1842 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1843 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1845 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1849 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1851 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1855 // ----------------------------------------------------------------------------
1856 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1857 // ----------------------------------------------------------------------------
1859 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1860 int width, int height, Uint32 color)
1864 for (y = src_y; y < src_y + height; y++)
1866 for (x = src_x; x < src_x + width; x++)
1868 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1870 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1875 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1876 int src_x, int src_y, int width, int height,
1877 int dst_x, int dst_y)
1881 for (y = 0; y < height; y++)
1883 for (x = 0; x < width; x++)
1885 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1887 if (pixel != BLACK_PIXEL)
1888 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1894 // ============================================================================
1895 // The following functions were taken from the SDL_gfx library version 2.0.3
1896 // (Rotozoomer) by Andreas Schiffler
1897 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1898 // ============================================================================
1900 // ----------------------------------------------------------------------------
1903 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1904 // ----------------------------------------------------------------------------
1914 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1917 tColorRGBA *sp, *csp, *dp;
1921 sp = csp = (tColorRGBA *) src->pixels;
1922 dp = (tColorRGBA *) dst->pixels;
1923 dgap = dst->pitch - dst->w * 4;
1925 for (y = 0; y < dst->h; y++)
1929 for (x = 0; x < dst->w; x++)
1931 tColorRGBA *sp0 = sp;
1932 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1933 tColorRGBA *sp00 = &sp0[0];
1934 tColorRGBA *sp01 = &sp0[1];
1935 tColorRGBA *sp10 = &sp1[0];
1936 tColorRGBA *sp11 = &sp1[1];
1939 // create new color pixel from all four source color pixels
1940 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1941 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1942 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1943 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1948 // advance source pointers
1951 // advance destination pointer
1955 // advance source pointer
1956 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1958 // advance destination pointers
1959 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1965 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1967 int x, y, *sax, *say, *csax, *csay;
1969 tColorRGBA *sp, *csp, *csp0, *dp;
1972 // use specialized zoom function when scaling down to exactly half size
1973 if (src->w == 2 * dst->w &&
1974 src->h == 2 * dst->h)
1975 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1978 sx = (float) src->w / (float) dst->w;
1979 sy = (float) src->h / (float) dst->h;
1981 // allocate memory for row increments
1982 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1983 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1985 // precalculate row increments
1986 for (x = 0; x <= dst->w; x++)
1987 *csax++ = (int)(sx * x);
1989 for (y = 0; y <= dst->h; y++)
1990 *csay++ = (int)(sy * y);
1993 sp = csp = csp0 = (tColorRGBA *) src->pixels;
1994 dp = (tColorRGBA *) dst->pixels;
1995 dgap = dst->pitch - dst->w * 4;
1998 for (y = 0; y < dst->h; y++)
2003 for (x = 0; x < dst->w; x++)
2008 // advance source pointers
2012 // advance destination pointer
2016 // advance source pointer
2018 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2020 // advance destination pointers
2021 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2030 // ----------------------------------------------------------------------------
2033 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2034 // ----------------------------------------------------------------------------
2036 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2038 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2039 Uint8 *sp, *dp, *csp;
2043 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2044 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2046 // allocate memory for row increments
2047 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2048 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2050 // precalculate row increments
2053 for (x = 0; x < dst->w; x++)
2056 *csax = (csx >> 16);
2063 for (y = 0; y < dst->h; y++)
2066 *csay = (csy >> 16);
2073 for (x = 0; x < dst->w; x++)
2081 for (y = 0; y < dst->h; y++)
2088 sp = csp = (Uint8 *) src->pixels;
2089 dp = (Uint8 *) dst->pixels;
2090 dgap = dst->pitch - dst->w;
2094 for (y = 0; y < dst->h; y++)
2098 for (x = 0; x < dst->w; x++)
2103 // advance source pointers
2107 // advance destination pointer
2111 // advance source pointer (for row)
2112 csp += ((*csay) * src->pitch);
2115 // advance destination pointers
2125 // ----------------------------------------------------------------------------
2128 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2129 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2130 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2131 // into a 32bit RGBA format on the fly.
2132 // ----------------------------------------------------------------------------
2134 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2136 SDL_Surface *zoom_src = NULL;
2137 SDL_Surface *zoom_dst = NULL;
2138 boolean is_converted = FALSE;
2145 // determine if source surface is 32 bit or 8 bit
2146 is_32bit = (src->format->BitsPerPixel == 32);
2148 if (is_32bit || src->format->BitsPerPixel == 8)
2150 // use source surface 'as is'
2155 // new source surface is 32 bit with a defined RGB ordering
2156 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2157 0x000000ff, 0x0000ff00, 0x00ff0000,
2158 (src->format->Amask ? 0xff000000 : 0));
2159 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2161 is_converted = TRUE;
2164 // allocate surface to completely contain the zoomed surface
2167 // target surface is 32 bit with source RGBA/ABGR ordering
2168 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2169 zoom_src->format->Rmask,
2170 zoom_src->format->Gmask,
2171 zoom_src->format->Bmask,
2172 zoom_src->format->Amask);
2176 // target surface is 8 bit
2177 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2181 // lock source surface
2182 SDL_LockSurface(zoom_src);
2184 // check which kind of surface we have
2187 // call the 32 bit transformation routine to do the zooming
2188 zoomSurfaceRGBA(zoom_src, zoom_dst);
2193 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2194 zoom_dst->format->palette->colors[i] =
2195 zoom_src->format->palette->colors[i];
2196 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2198 // call the 8 bit transformation routine to do the zooming
2199 zoomSurfaceY(zoom_src, zoom_dst);
2202 // unlock source surface
2203 SDL_UnlockSurface(zoom_src);
2205 // free temporary surface
2207 SDL_FreeSurface(zoom_src);
2209 // return destination surface
2213 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2215 SDL_Surface *new_surface;
2217 if (surface == NULL)
2220 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2221 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2223 // remove alpha channel from native non-transparent surface, if defined
2224 SDLSetAlpha(new_surface, FALSE, 0);
2226 // remove transparent color from native non-transparent surface, if defined
2227 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2232 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2234 Bitmap *dst_bitmap = CreateBitmapStruct();
2235 SDL_Surface *src_surface = src_bitmap->surface_masked;
2236 SDL_Surface *dst_surface;
2238 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2239 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2241 dst_bitmap->width = dst_width;
2242 dst_bitmap->height = dst_height;
2244 // create zoomed temporary surface from source surface
2245 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2247 // create native format destination surface from zoomed temporary surface
2248 SDLSetNativeSurface(&dst_surface);
2250 // set color key for zoomed surface from source surface, if defined
2251 if (SDLHasColorKey(src_surface))
2252 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2253 SDLGetColorKey(src_surface));
2255 // create native non-transparent surface for opaque blitting
2256 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2258 // set native transparent surface for masked blitting
2259 dst_bitmap->surface_masked = dst_surface;
2265 // ============================================================================
2266 // load image to bitmap
2267 // ============================================================================
2269 Bitmap *SDLLoadImage(char *filename)
2271 Bitmap *new_bitmap = CreateBitmapStruct();
2272 SDL_Surface *sdl_image_tmp;
2274 if (program.headless)
2276 // prevent sanity check warnings at later stage
2277 new_bitmap->width = new_bitmap->height = 1;
2282 print_timestamp_init("SDLLoadImage");
2284 print_timestamp_time(getBaseNamePtr(filename));
2286 // load image to temporary surface
2287 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2288 Error(ERR_EXIT, "IMG_Load('%s') failed: %s", getBaseNamePtr(filename),
2291 print_timestamp_time("IMG_Load");
2293 UPDATE_BUSY_STATE();
2295 // create native non-transparent surface for current image
2296 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2297 Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2299 print_timestamp_time("SDLGetNativeSurface (opaque)");
2301 UPDATE_BUSY_STATE();
2303 // set black pixel to transparent if no alpha channel / transparent color
2304 if (!SDLHasAlpha(sdl_image_tmp) &&
2305 !SDLHasColorKey(sdl_image_tmp))
2306 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2307 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2309 // create native transparent surface for current image
2310 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2311 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2313 print_timestamp_time("SDLGetNativeSurface (masked)");
2315 UPDATE_BUSY_STATE();
2317 // free temporary surface
2318 SDL_FreeSurface(sdl_image_tmp);
2320 new_bitmap->width = new_bitmap->surface->w;
2321 new_bitmap->height = new_bitmap->surface->h;
2323 print_timestamp_done("SDLLoadImage");
2329 // ----------------------------------------------------------------------------
2330 // custom cursor fuctions
2331 // ----------------------------------------------------------------------------
2333 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2335 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2336 cursor_info->width, cursor_info->height,
2337 cursor_info->hot_x, cursor_info->hot_y);
2340 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2342 static struct MouseCursorInfo *last_cursor_info = NULL;
2343 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2344 static SDL_Cursor *cursor_default = NULL;
2345 static SDL_Cursor *cursor_current = NULL;
2347 // if invoked for the first time, store the SDL default cursor
2348 if (cursor_default == NULL)
2349 cursor_default = SDL_GetCursor();
2351 // only create new cursor if cursor info (custom only) has changed
2352 if (cursor_info != NULL && cursor_info != last_cursor_info)
2354 cursor_current = create_cursor(cursor_info);
2355 last_cursor_info = cursor_info;
2358 // only set new cursor if cursor info (custom or NULL) has changed
2359 if (cursor_info != last_cursor_info2)
2360 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2362 last_cursor_info2 = cursor_info;
2366 // ============================================================================
2368 // ============================================================================
2370 void SDLOpenAudio(void)
2372 if (program.headless)
2375 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2377 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2381 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2382 AUDIO_NUM_CHANNELS_STEREO,
2383 setup.system.audio_fragment_size) < 0)
2385 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2389 audio.sound_available = TRUE;
2390 audio.music_available = TRUE;
2391 audio.loops_available = TRUE;
2392 audio.sound_enabled = TRUE;
2394 // set number of available mixer channels
2395 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2396 audio.music_channel = MUSIC_CHANNEL;
2397 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2399 Mixer_InitChannels();
2402 void SDLCloseAudio(void)
2405 Mix_HaltChannel(-1);
2408 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2412 // ============================================================================
2414 // ============================================================================
2416 void SDLWaitEvent(Event *event)
2418 SDL_WaitEvent(event);
2421 void SDLCorrectRawMousePosition(int *x, int *y)
2423 if (sdl_renderer == NULL)
2426 // this corrects the raw mouse position for logical screen size within event
2427 // filters (correction done later by SDL library when handling mouse events)
2430 float scale_x, scale_y;
2432 SDL_RenderGetViewport(sdl_renderer, &viewport);
2433 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2435 *x = (int)(*x / scale_x);
2436 *y = (int)(*y / scale_y);
2443 // ============================================================================
2444 // joystick functions
2445 // ============================================================================
2447 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2448 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2449 static int sdl_js_axis[MAX_PLAYERS][2];
2450 static int sdl_js_button[MAX_PLAYERS][2];
2451 static boolean sdl_is_controller[MAX_PLAYERS];
2453 void SDLClearJoystickState(void)
2457 for (i = 0; i < MAX_PLAYERS; i++)
2459 for (j = 0; j < 2; j++)
2461 sdl_js_axis_raw[i][j] = -1;
2462 sdl_js_axis[i][j] = 0;
2463 sdl_js_button[i][j] = 0;
2468 boolean SDLOpenJoystick(int nr)
2470 if (nr < 0 || nr >= MAX_PLAYERS)
2473 sdl_is_controller[nr] = SDL_IsGameController(nr);
2476 Error(ERR_DEBUG, "opening joystick %d (%s)",
2477 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2480 if (sdl_is_controller[nr])
2481 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2483 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2485 return (sdl_joystick[nr] != NULL);
2488 void SDLCloseJoystick(int nr)
2490 if (nr < 0 || nr >= MAX_PLAYERS)
2494 Error(ERR_DEBUG, "closing joystick %d", nr);
2497 if (sdl_is_controller[nr])
2498 SDL_GameControllerClose(sdl_joystick[nr]);
2500 SDL_JoystickClose(sdl_joystick[nr]);
2502 sdl_joystick[nr] = NULL;
2505 boolean SDLCheckJoystickOpened(int nr)
2507 if (nr < 0 || nr >= MAX_PLAYERS)
2510 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2513 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2515 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2516 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2517 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2518 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2520 if (nr < 0 || nr >= MAX_PLAYERS)
2526 // prevent (slightly jittering, but centered) axis A from resetting axis B
2527 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2528 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2531 sdl_js_axis[nr][axis_id] = axis_value;
2532 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2535 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2537 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2538 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2539 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2540 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2541 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2542 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2543 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2544 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2547 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2548 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2549 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2550 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2551 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2552 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2553 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2554 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2556 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2557 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2558 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2559 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2560 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2562 if (nr < 0 || nr >= MAX_PLAYERS)
2565 if (button_id == -1)
2568 sdl_js_button[nr][button_id] = button_state;
2571 void HandleJoystickEvent(Event *event)
2573 // when using joystick, disable overlay touch buttons
2574 runtime.uses_touch_device = FALSE;
2576 switch (event->type)
2578 case SDL_CONTROLLERDEVICEADDED:
2580 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2581 event->cdevice.which);
2586 case SDL_CONTROLLERDEVICEREMOVED:
2588 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2589 event->cdevice.which);
2594 case SDL_CONTROLLERAXISMOTION:
2596 Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2597 event->caxis.which, event->caxis.axis, event->caxis.value);
2599 setJoystickAxis(event->caxis.which,
2601 event->caxis.value);
2604 case SDL_CONTROLLERBUTTONDOWN:
2606 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2607 event->cbutton.which, event->cbutton.button);
2609 setJoystickButton(event->cbutton.which,
2610 event->cbutton.button,
2614 case SDL_CONTROLLERBUTTONUP:
2616 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2617 event->cbutton.which, event->cbutton.button);
2619 setJoystickButton(event->cbutton.which,
2620 event->cbutton.button,
2624 case SDL_JOYAXISMOTION:
2625 if (sdl_is_controller[event->jaxis.which])
2629 Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2630 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2632 if (event->jaxis.axis < 4)
2633 setJoystickAxis(event->jaxis.which,
2635 event->jaxis.value);
2638 case SDL_JOYBUTTONDOWN:
2639 if (sdl_is_controller[event->jaxis.which])
2643 Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2644 event->jbutton.which, event->jbutton.button);
2646 if (event->jbutton.button < 4)
2647 setJoystickButton(event->jbutton.which,
2648 event->jbutton.button,
2652 case SDL_JOYBUTTONUP:
2653 if (sdl_is_controller[event->jaxis.which])
2657 Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2658 event->jbutton.which, event->jbutton.button);
2660 if (event->jbutton.button < 4)
2661 setJoystickButton(event->jbutton.which,
2662 event->jbutton.button,
2671 void SDLInitJoysticks(void)
2673 static boolean sdl_joystick_subsystem_initialized = FALSE;
2674 boolean print_warning = !sdl_joystick_subsystem_initialized;
2675 char *mappings_file_base = getPath2(options.conf_directory,
2676 GAMECONTROLLER_BASENAME);
2677 char *mappings_file_user = getPath2(getUserGameDataDir(),
2678 GAMECONTROLLER_BASENAME);
2682 if (!sdl_joystick_subsystem_initialized)
2684 sdl_joystick_subsystem_initialized = TRUE;
2686 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2688 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2690 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2694 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2696 // the included game controller base mappings should always be found
2697 if (num_mappings == -1)
2698 Error(ERR_WARN, "no game controller base mappings found");
2701 Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2704 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2707 // the personal game controller user mappings may or may not be found
2708 if (num_mappings == -1)
2709 Error(ERR_WARN, "no game controller user mappings found");
2711 Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2713 Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2716 checked_free(mappings_file_base);
2717 checked_free(mappings_file_user);
2720 for (i = 0; i < SDL_NumJoysticks(); i++)
2722 const char *name, *type;
2724 if (SDL_IsGameController(i))
2726 name = SDL_GameControllerNameForIndex(i);
2727 type = "game controller";
2731 name = SDL_JoystickNameForIndex(i);
2735 Error(ERR_INFO, "- joystick %d (%s): '%s'",
2736 i, type, (name ? name : "(Unknown)"));
2741 // assign joysticks from configured to connected joystick for all players
2742 for (i = 0; i < MAX_PLAYERS; i++)
2744 // get configured joystick for this player
2745 char *device_name = setup.input[i].joy.device_name;
2746 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2748 if (joystick_nr >= SDL_NumJoysticks())
2750 if (setup.input[i].use_joystick && print_warning)
2751 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2756 // store configured joystick number for each player
2757 joystick.nr[i] = joystick_nr;
2760 // now open all connected joysticks (regardless if configured or not)
2761 for (i = 0; i < SDL_NumJoysticks(); i++)
2763 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2764 if (SDLCheckJoystickOpened(i))
2765 SDLCloseJoystick(i);
2767 if (SDLOpenJoystick(i))
2768 joystick.status = JOYSTICK_ACTIVATED;
2769 else if (print_warning)
2770 Error(ERR_WARN, "cannot open joystick %d", i);
2773 SDLClearJoystickState();
2776 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2778 if (nr < 0 || nr >= MAX_PLAYERS)
2782 *x = sdl_js_axis[nr][0];
2784 *y = sdl_js_axis[nr][1];
2787 *b1 = sdl_js_button[nr][0];
2789 *b2 = sdl_js_button[nr][1];
2795 // ============================================================================
2796 // touch input overlay functions
2797 // ============================================================================
2799 #if defined(USE_TOUCH_INPUT_OVERLAY)
2800 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2803 int grid_xsize = overlay.grid_xsize;
2804 int grid_ysize = overlay.grid_ysize;
2807 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2808 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2810 for (x = 0; x < grid_xsize; x++)
2812 rect.x = (x + 0) * video.screen_width / grid_xsize;
2813 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2815 for (y = 0; y < grid_ysize; y++)
2817 rect.y = (y + 0) * video.screen_height / grid_ysize;
2818 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2820 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2821 SDL_RenderDrawRect(sdl_renderer, &rect);
2825 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2828 static void RenderFillRectangle(int x, int y, int width, int height)
2830 SDL_Rect rect = { x, y, width, height };
2832 SDL_RenderFillRect(sdl_renderer, &rect);
2835 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2837 static int alpha_direction = 0;
2838 static int alpha_highlight = 0;
2839 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2840 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2842 int grid_xsize = overlay.grid_xsize;
2843 int grid_ysize = overlay.grid_ysize;
2846 if (alpha == alpha_max)
2848 if (alpha_direction < 0)
2850 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2852 if (alpha_highlight == 0)
2853 alpha_direction = 1;
2857 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2859 if (alpha_highlight == alpha_max)
2860 alpha_direction = -1;
2865 alpha_direction = 1;
2866 alpha_highlight = alpha;
2869 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2871 for (x = 0; x < grid_xsize; x++)
2873 for (y = 0; y < grid_ysize; y++)
2875 int grid_button = overlay.grid_button[x][y];
2876 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2877 int alpha_draw = alpha;
2878 int outline_border = MV_NONE;
2879 int border_size = 2;
2880 boolean draw_outlined = setup.touch.draw_outlined;
2881 boolean draw_pressed = setup.touch.draw_pressed;
2883 if (grid_button == CHAR_GRID_BUTTON_NONE)
2886 if (grid_button == overlay.grid_button_highlight)
2888 draw_outlined = FALSE;
2889 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2892 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2895 draw_outlined = FALSE;
2897 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2900 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2902 rect.x = (x + 0) * video.screen_width / grid_xsize;
2903 rect.y = (y + 0) * video.screen_height / grid_ysize;
2904 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2905 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2907 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2909 rect.x += border_size;
2910 rect.w -= border_size;
2912 outline_border |= MV_LEFT;
2915 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2917 rect.w -= border_size;
2919 outline_border |= MV_RIGHT;
2922 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2924 rect.y += border_size;
2925 rect.h -= border_size;
2927 outline_border |= MV_UP;
2930 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2932 rect.h -= border_size;
2934 outline_border |= MV_DOWN;
2939 int rect_x = rect.x +
2940 (outline_border & MV_LEFT ? border_size : 0);
2941 int rect_w = rect.w -
2942 (outline_border & MV_LEFT ? border_size : 0) -
2943 (outline_border & MV_RIGHT ? border_size : 0);
2945 if (outline_border & MV_LEFT)
2946 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2948 if (outline_border & MV_RIGHT)
2949 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2950 border_size, rect.h);
2952 if (outline_border & MV_UP)
2953 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2955 if (outline_border & MV_DOWN)
2956 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
2957 rect_w, border_size);
2961 SDL_RenderFillRect(sdl_renderer, &rect);
2966 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2969 static void DrawTouchInputOverlay(void)
2971 static boolean deactivated = TRUE;
2972 static boolean show_grid = FALSE;
2973 static int alpha = 0;
2974 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2975 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2976 boolean active = (overlay.enabled && overlay.active);
2978 if (!active && deactivated)
2983 if (alpha < alpha_max)
2984 alpha = MIN(alpha + alpha_step, alpha_max);
2986 deactivated = FALSE;
2990 alpha = MAX(0, alpha - alpha_step);
2996 if (overlay.show_grid)
2998 else if (deactivated)
3002 DrawTouchInputOverlay_ShowGrid(alpha);
3004 DrawTouchInputOverlay_ShowGridButtons(alpha);
3007 static void DrawTouchGadgetsOverlay(void)
3009 DrawGadgets_OverlayTouchButtons();