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)
880 interval = VSYNC_MODE_NORMAL;
882 result = SDL_GL_SetSwapInterval(interval);
886 interval = VSYNC_MODE_OFF;
888 video.vsync_mode = interval;
891 void SDLRedrawWindow(void)
893 UpdateScreen_WithoutFrameDelay(NULL);
896 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
899 if (program.headless)
902 SDL_Surface *surface =
903 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
906 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
908 SDLSetNativeSurface(&surface);
910 bitmap->surface = surface;
913 void SDLFreeBitmapPointers(Bitmap *bitmap)
916 SDL_FreeSurface(bitmap->surface);
917 if (bitmap->surface_masked)
918 SDL_FreeSurface(bitmap->surface_masked);
920 bitmap->surface = NULL;
921 bitmap->surface_masked = NULL;
924 SDL_DestroyTexture(bitmap->texture);
925 if (bitmap->texture_masked)
926 SDL_DestroyTexture(bitmap->texture_masked);
928 bitmap->texture = NULL;
929 bitmap->texture_masked = NULL;
932 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
933 int src_x, int src_y, int width, int height,
934 int dst_x, int dst_y, int mask_mode)
936 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
937 SDL_Rect src_rect, dst_rect;
949 // if (src_bitmap != backbuffer || dst_bitmap != window)
950 if (!(src_bitmap == backbuffer && dst_bitmap == window))
951 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
952 src_bitmap->surface_masked : src_bitmap->surface),
953 &src_rect, real_dst_bitmap->surface, &dst_rect);
955 if (dst_bitmap == window)
956 UpdateScreen_WithFrameDelay(&dst_rect);
959 void SDLBlitTexture(Bitmap *bitmap,
960 int src_x, int src_y, int width, int height,
961 int dst_x, int dst_y, int mask_mode)
963 SDL_Texture *texture;
968 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
983 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
986 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
989 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
997 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
999 if (dst_bitmap == window)
1000 UpdateScreen_WithFrameDelay(&rect);
1003 void PrepareFadeBitmap(int draw_target)
1005 Bitmap *fade_bitmap =
1006 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1007 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1009 if (fade_bitmap == NULL)
1012 // copy backbuffer to fading buffer
1013 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1015 // add border and animations to fading buffer
1016 FinalizeScreen(draw_target);
1019 void SDLFadeRectangle(int x, int y, int width, int height,
1020 int fade_mode, int fade_delay, int post_delay,
1021 void (*draw_border_function)(void))
1023 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1024 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1025 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1026 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1027 SDL_Surface *surface_screen = backbuffer->surface;
1028 SDL_Rect src_rect, dst_rect;
1030 int src_x = x, src_y = y;
1031 int dst_x = x, dst_y = y;
1032 unsigned int time_last, time_current;
1034 // store function for drawing global masked border
1035 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1037 // deactivate drawing of global border while fading, if needed
1038 if (draw_border_function == NULL)
1039 gfx.draw_global_border_function = NULL;
1044 src_rect.h = height;
1048 dst_rect.w = width; // (ignored)
1049 dst_rect.h = height; // (ignored)
1051 dst_rect2 = dst_rect;
1053 // before fading in, store backbuffer (without animation graphics)
1054 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1055 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1057 // copy source and target surfaces to temporary surfaces for fading
1058 if (fade_mode & FADE_TYPE_TRANSFORM)
1060 // (source and target fading buffer already prepared)
1062 else if (fade_mode & FADE_TYPE_FADE_IN)
1064 // (target fading buffer already prepared)
1065 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1067 else // FADE_TYPE_FADE_OUT
1069 // (source fading buffer already prepared)
1070 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1073 time_current = SDL_GetTicks();
1075 if (fade_delay <= 0)
1077 // immediately draw final target frame without delay
1078 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1082 // when fading without delay, also skip post delay
1086 if (fade_mode == FADE_MODE_MELT)
1088 boolean done = FALSE;
1089 int melt_pixels = 2;
1090 int melt_columns = width / melt_pixels;
1091 int ypos[melt_columns];
1092 int max_steps = height / 8 + 32;
1097 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1099 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1101 ypos[0] = -GetSimpleRandom(16);
1103 for (i = 1 ; i < melt_columns; i++)
1105 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1107 ypos[i] = ypos[i - 1] + r;
1120 time_last = time_current;
1121 time_current = SDL_GetTicks();
1122 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1123 steps_final = MIN(MAX(0, steps), max_steps);
1127 done = (steps_done >= steps_final);
1129 for (i = 0 ; i < melt_columns; i++)
1137 else if (ypos[i] < height)
1142 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1144 if (ypos[i] + dy >= height)
1145 dy = height - ypos[i];
1147 // copy part of (appearing) target surface to upper area
1148 src_rect.x = src_x + i * melt_pixels;
1149 // src_rect.y = src_y + ypos[i];
1151 src_rect.w = melt_pixels;
1153 src_rect.h = ypos[i] + dy;
1155 dst_rect.x = dst_x + i * melt_pixels;
1156 // dst_rect.y = dst_y + ypos[i];
1159 if (steps_done >= steps_final)
1160 SDL_BlitSurface(surface_target, &src_rect,
1161 surface_screen, &dst_rect);
1165 // copy part of (disappearing) source surface to lower area
1166 src_rect.x = src_x + i * melt_pixels;
1168 src_rect.w = melt_pixels;
1169 src_rect.h = height - ypos[i];
1171 dst_rect.x = dst_x + i * melt_pixels;
1172 dst_rect.y = dst_y + ypos[i];
1174 if (steps_done >= steps_final)
1175 SDL_BlitSurface(surface_source, &src_rect,
1176 surface_screen, &dst_rect);
1182 src_rect.x = src_x + i * melt_pixels;
1184 src_rect.w = melt_pixels;
1185 src_rect.h = height;
1187 dst_rect.x = dst_x + i * melt_pixels;
1190 if (steps_done >= steps_final)
1191 SDL_BlitSurface(surface_target, &src_rect,
1192 surface_screen, &dst_rect);
1196 if (steps_done >= steps_final)
1198 if (draw_border_function != NULL)
1199 draw_border_function();
1201 UpdateScreen_WithFrameDelay(&dst_rect2);
1205 else if (fade_mode == FADE_MODE_CURTAIN)
1209 int xx_size = width / 2;
1211 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1213 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1215 for (xx = 0; xx < xx_size;)
1217 time_last = time_current;
1218 time_current = SDL_GetTicks();
1219 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1220 xx_final = MIN(MAX(0, xx), xx_size);
1225 src_rect.h = height;
1230 // draw new (target) image to screen buffer
1231 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1233 if (xx_final < xx_size)
1235 src_rect.w = xx_size - xx_final;
1236 src_rect.h = height;
1238 // draw old (source) image to screen buffer (left side)
1240 src_rect.x = src_x + xx_final;
1243 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1245 // draw old (source) image to screen buffer (right side)
1247 src_rect.x = src_x + xx_size;
1248 dst_rect.x = dst_x + xx_size + xx_final;
1250 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1253 if (draw_border_function != NULL)
1254 draw_border_function();
1256 // only update the region of the screen that is affected from fading
1257 UpdateScreen_WithFrameDelay(&dst_rect2);
1260 else // fading in, fading out or cross-fading
1265 for (alpha = 0.0; alpha < 255.0;)
1267 time_last = time_current;
1268 time_current = SDL_GetTicks();
1269 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1270 alpha_final = MIN(MAX(0, alpha), 255);
1272 // draw existing (source) image to screen buffer
1273 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1275 // draw new (target) image to screen buffer using alpha blending
1276 SDLSetAlpha(surface_target, TRUE, alpha_final);
1277 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1279 if (draw_border_function != NULL)
1280 draw_border_function();
1282 // only update the region of the screen that is affected from fading
1283 UpdateScreen_WithFrameDelay(&dst_rect);
1288 Delay_WithScreenUpdates(post_delay);
1290 // restore function for drawing global masked border
1291 gfx.draw_global_border_function = draw_global_border_function;
1293 // after fading in, restore backbuffer (without animation graphics)
1294 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1295 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1298 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1299 int to_x, int to_y, Uint32 color)
1301 SDL_Surface *surface = dst_bitmap->surface;
1305 swap_numbers(&from_x, &to_x);
1308 swap_numbers(&from_y, &to_y);
1312 rect.w = (to_x - from_x + 1);
1313 rect.h = (to_y - from_y + 1);
1315 SDL_FillRect(surface, &rect, color);
1318 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1319 int to_x, int to_y, Uint32 color)
1321 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1324 #if ENABLE_UNUSED_CODE
1325 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1326 int num_points, Uint32 color)
1331 for (i = 0; i < num_points - 1; i++)
1333 for (x = 0; x < line_width; x++)
1335 for (y = 0; y < line_width; y++)
1337 int dx = x - line_width / 2;
1338 int dy = y - line_width / 2;
1340 if ((x == 0 && y == 0) ||
1341 (x == 0 && y == line_width - 1) ||
1342 (x == line_width - 1 && y == 0) ||
1343 (x == line_width - 1 && y == line_width - 1))
1346 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1347 points[i+1].x + dx, points[i+1].y + dy, color);
1354 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1356 SDL_Surface *surface = src_bitmap->surface;
1358 switch (surface->format->BytesPerPixel)
1360 case 1: // assuming 8-bpp
1362 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1366 case 2: // probably 15-bpp or 16-bpp
1368 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1372 case 3: // slow 24-bpp mode; usually not used
1375 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1379 shift = surface->format->Rshift;
1380 color |= *(pix + shift / 8) >> shift;
1381 shift = surface->format->Gshift;
1382 color |= *(pix + shift / 8) >> shift;
1383 shift = surface->format->Bshift;
1384 color |= *(pix + shift / 8) >> shift;
1390 case 4: // probably 32-bpp
1392 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1401 // ============================================================================
1402 // The following functions were taken from the SGE library
1403 // (SDL Graphics Extension Library) by Anders Lindström
1404 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1405 // ============================================================================
1407 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1409 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1411 switch (surface->format->BytesPerPixel)
1416 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1422 // Probably 15-bpp or 16-bpp
1423 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1429 // Slow 24-bpp mode, usually not used
1433 // Gack - slow, but endian correct
1434 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1435 shift = surface->format->Rshift;
1436 *(pix+shift/8) = color>>shift;
1437 shift = surface->format->Gshift;
1438 *(pix+shift/8) = color>>shift;
1439 shift = surface->format->Bshift;
1440 *(pix+shift/8) = color>>shift;
1447 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1455 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1456 Uint8 R, Uint8 G, Uint8 B)
1458 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1461 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1463 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1466 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1468 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1471 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1476 // Gack - slow, but endian correct
1477 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1478 shift = surface->format->Rshift;
1479 *(pix+shift/8) = color>>shift;
1480 shift = surface->format->Gshift;
1481 *(pix+shift/8) = color>>shift;
1482 shift = surface->format->Bshift;
1483 *(pix+shift/8) = color>>shift;
1486 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1488 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1491 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1493 switch (dest->format->BytesPerPixel)
1496 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1500 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1504 _PutPixel24(dest,x,y,color);
1508 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1514 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1516 if (SDL_MUSTLOCK(surface))
1518 if (SDL_LockSurface(surface) < 0)
1524 _PutPixel(surface, x, y, color);
1526 if (SDL_MUSTLOCK(surface))
1528 SDL_UnlockSurface(surface);
1533 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1534 Uint8 r, Uint8 g, Uint8 b)
1536 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1539 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1541 if (y >= 0 && y <= dest->h - 1)
1543 switch (dest->format->BytesPerPixel)
1546 return y*dest->pitch;
1550 return y*dest->pitch/2;
1554 return y*dest->pitch;
1558 return y*dest->pitch/4;
1566 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1569 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1571 switch (surface->format->BytesPerPixel)
1576 *((Uint8 *)surface->pixels + ypitch + x) = color;
1582 // Probably 15-bpp or 16-bpp
1583 *((Uint16 *)surface->pixels + ypitch + x) = color;
1589 // Slow 24-bpp mode, usually not used
1593 // Gack - slow, but endian correct
1594 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1595 shift = surface->format->Rshift;
1596 *(pix+shift/8) = color>>shift;
1597 shift = surface->format->Gshift;
1598 *(pix+shift/8) = color>>shift;
1599 shift = surface->format->Bshift;
1600 *(pix+shift/8) = color>>shift;
1607 *((Uint32 *)surface->pixels + ypitch + x) = color;
1614 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1619 if (SDL_MUSTLOCK(Surface))
1621 if (SDL_LockSurface(Surface) < 0)
1635 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1639 if (x2 > Surface->w - 1)
1640 x2 = Surface->w - 1;
1647 SDL_FillRect(Surface, &l, Color);
1649 if (SDL_MUSTLOCK(Surface))
1651 SDL_UnlockSurface(Surface);
1655 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1656 Uint8 R, Uint8 G, Uint8 B)
1658 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1661 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1674 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1678 if (x2 > Surface->w - 1)
1679 x2 = Surface->w - 1;
1686 SDL_FillRect(Surface, &l, Color);
1689 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1694 if (SDL_MUSTLOCK(Surface))
1696 if (SDL_LockSurface(Surface) < 0)
1710 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1714 if (y2 > Surface->h - 1)
1715 y2 = Surface->h - 1;
1722 SDL_FillRect(Surface, &l, Color);
1724 if (SDL_MUSTLOCK(Surface))
1726 SDL_UnlockSurface(Surface);
1730 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1731 Uint8 R, Uint8 G, Uint8 B)
1733 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1736 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1749 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1753 if (y2 > Surface->h - 1)
1754 y2 = Surface->h - 1;
1761 SDL_FillRect(Surface, &l, Color);
1765 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1766 Sint16 x2, Sint16 y2, Uint32 Color,
1767 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1770 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1775 sdx = (dx < 0) ? -1 : 1;
1776 sdy = (dy < 0) ? -1 : 1;
1788 for (x = 0; x < dx; x++)
1790 Callback(Surface, px, py, Color);
1804 for (y = 0; y < dy; y++)
1806 Callback(Surface, px, py, Color);
1821 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1822 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1823 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1826 sge_DoLine(Surface, X1, Y1, X2, Y2,
1827 SDL_MapRGB(Surface->format, R, G, B), Callback);
1831 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1834 if (SDL_MUSTLOCK(Surface))
1836 if (SDL_LockSurface(Surface) < 0)
1841 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1843 // unlock the display
1844 if (SDL_MUSTLOCK(Surface))
1846 SDL_UnlockSurface(Surface);
1851 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1852 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1854 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1858 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1860 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1864 // ----------------------------------------------------------------------------
1865 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1866 // ----------------------------------------------------------------------------
1868 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1869 int width, int height, Uint32 color)
1873 for (y = src_y; y < src_y + height; y++)
1875 for (x = src_x; x < src_x + width; x++)
1877 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1879 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1884 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1885 int src_x, int src_y, int width, int height,
1886 int dst_x, int dst_y)
1890 for (y = 0; y < height; y++)
1892 for (x = 0; x < width; x++)
1894 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1896 if (pixel != BLACK_PIXEL)
1897 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1903 // ============================================================================
1904 // The following functions were taken from the SDL_gfx library version 2.0.3
1905 // (Rotozoomer) by Andreas Schiffler
1906 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1907 // ============================================================================
1909 // ----------------------------------------------------------------------------
1912 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1913 // ----------------------------------------------------------------------------
1923 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1926 tColorRGBA *sp, *csp, *dp;
1930 sp = csp = (tColorRGBA *) src->pixels;
1931 dp = (tColorRGBA *) dst->pixels;
1932 dgap = dst->pitch - dst->w * 4;
1934 for (y = 0; y < dst->h; y++)
1938 for (x = 0; x < dst->w; x++)
1940 tColorRGBA *sp0 = sp;
1941 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1942 tColorRGBA *sp00 = &sp0[0];
1943 tColorRGBA *sp01 = &sp0[1];
1944 tColorRGBA *sp10 = &sp1[0];
1945 tColorRGBA *sp11 = &sp1[1];
1948 // create new color pixel from all four source color pixels
1949 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1950 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1951 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1952 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1957 // advance source pointers
1960 // advance destination pointer
1964 // advance source pointer
1965 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1967 // advance destination pointers
1968 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1974 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1976 int x, y, *sax, *say, *csax, *csay;
1978 tColorRGBA *sp, *csp, *csp0, *dp;
1981 // use specialized zoom function when scaling down to exactly half size
1982 if (src->w == 2 * dst->w &&
1983 src->h == 2 * dst->h)
1984 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1987 sx = (float) src->w / (float) dst->w;
1988 sy = (float) src->h / (float) dst->h;
1990 // allocate memory for row increments
1991 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1992 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1994 // precalculate row increments
1995 for (x = 0; x <= dst->w; x++)
1996 *csax++ = (int)(sx * x);
1998 for (y = 0; y <= dst->h; y++)
1999 *csay++ = (int)(sy * y);
2002 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2003 dp = (tColorRGBA *) dst->pixels;
2004 dgap = dst->pitch - dst->w * 4;
2007 for (y = 0; y < dst->h; y++)
2012 for (x = 0; x < dst->w; x++)
2017 // advance source pointers
2021 // advance destination pointer
2025 // advance source pointer
2027 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2029 // advance destination pointers
2030 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2039 // ----------------------------------------------------------------------------
2042 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2043 // ----------------------------------------------------------------------------
2045 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2047 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2048 Uint8 *sp, *dp, *csp;
2052 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2053 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2055 // allocate memory for row increments
2056 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2057 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2059 // precalculate row increments
2062 for (x = 0; x < dst->w; x++)
2065 *csax = (csx >> 16);
2072 for (y = 0; y < dst->h; y++)
2075 *csay = (csy >> 16);
2082 for (x = 0; x < dst->w; x++)
2090 for (y = 0; y < dst->h; y++)
2097 sp = csp = (Uint8 *) src->pixels;
2098 dp = (Uint8 *) dst->pixels;
2099 dgap = dst->pitch - dst->w;
2103 for (y = 0; y < dst->h; y++)
2107 for (x = 0; x < dst->w; x++)
2112 // advance source pointers
2116 // advance destination pointer
2120 // advance source pointer (for row)
2121 csp += ((*csay) * src->pitch);
2124 // advance destination pointers
2134 // ----------------------------------------------------------------------------
2137 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2138 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2139 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2140 // into a 32bit RGBA format on the fly.
2141 // ----------------------------------------------------------------------------
2143 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2145 SDL_Surface *zoom_src = NULL;
2146 SDL_Surface *zoom_dst = NULL;
2147 boolean is_converted = FALSE;
2154 // determine if source surface is 32 bit or 8 bit
2155 is_32bit = (src->format->BitsPerPixel == 32);
2157 if (is_32bit || src->format->BitsPerPixel == 8)
2159 // use source surface 'as is'
2164 // new source surface is 32 bit with a defined RGB ordering
2165 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2166 0x000000ff, 0x0000ff00, 0x00ff0000,
2167 (src->format->Amask ? 0xff000000 : 0));
2168 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2170 is_converted = TRUE;
2173 // allocate surface to completely contain the zoomed surface
2176 // target surface is 32 bit with source RGBA/ABGR ordering
2177 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2178 zoom_src->format->Rmask,
2179 zoom_src->format->Gmask,
2180 zoom_src->format->Bmask,
2181 zoom_src->format->Amask);
2185 // target surface is 8 bit
2186 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2190 // lock source surface
2191 SDL_LockSurface(zoom_src);
2193 // check which kind of surface we have
2196 // call the 32 bit transformation routine to do the zooming
2197 zoomSurfaceRGBA(zoom_src, zoom_dst);
2202 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2203 zoom_dst->format->palette->colors[i] =
2204 zoom_src->format->palette->colors[i];
2205 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2207 // call the 8 bit transformation routine to do the zooming
2208 zoomSurfaceY(zoom_src, zoom_dst);
2211 // unlock source surface
2212 SDL_UnlockSurface(zoom_src);
2214 // free temporary surface
2216 SDL_FreeSurface(zoom_src);
2218 // return destination surface
2222 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2224 SDL_Surface *new_surface;
2226 if (surface == NULL)
2229 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2230 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2232 // remove alpha channel from native non-transparent surface, if defined
2233 SDLSetAlpha(new_surface, FALSE, 0);
2235 // remove transparent color from native non-transparent surface, if defined
2236 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2241 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2243 Bitmap *dst_bitmap = CreateBitmapStruct();
2244 SDL_Surface *src_surface = src_bitmap->surface_masked;
2245 SDL_Surface *dst_surface;
2247 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2248 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2250 dst_bitmap->width = dst_width;
2251 dst_bitmap->height = dst_height;
2253 // create zoomed temporary surface from source surface
2254 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2256 // create native format destination surface from zoomed temporary surface
2257 SDLSetNativeSurface(&dst_surface);
2259 // set color key for zoomed surface from source surface, if defined
2260 if (SDLHasColorKey(src_surface))
2261 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2262 SDLGetColorKey(src_surface));
2264 // create native non-transparent surface for opaque blitting
2265 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2267 // set native transparent surface for masked blitting
2268 dst_bitmap->surface_masked = dst_surface;
2274 // ============================================================================
2275 // load image to bitmap
2276 // ============================================================================
2278 Bitmap *SDLLoadImage(char *filename)
2280 Bitmap *new_bitmap = CreateBitmapStruct();
2281 SDL_Surface *sdl_image_tmp;
2283 if (program.headless)
2285 // prevent sanity check warnings at later stage
2286 new_bitmap->width = new_bitmap->height = 1;
2291 print_timestamp_init("SDLLoadImage");
2293 print_timestamp_time(getBaseNamePtr(filename));
2295 // load image to temporary surface
2296 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2297 Error(ERR_EXIT, "IMG_Load('%s') failed: %s", getBaseNamePtr(filename),
2300 print_timestamp_time("IMG_Load");
2302 UPDATE_BUSY_STATE();
2304 // create native non-transparent surface for current image
2305 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2306 Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2308 print_timestamp_time("SDLGetNativeSurface (opaque)");
2310 UPDATE_BUSY_STATE();
2312 // set black pixel to transparent if no alpha channel / transparent color
2313 if (!SDLHasAlpha(sdl_image_tmp) &&
2314 !SDLHasColorKey(sdl_image_tmp))
2315 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2316 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2318 // create native transparent surface for current image
2319 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2320 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2322 print_timestamp_time("SDLGetNativeSurface (masked)");
2324 UPDATE_BUSY_STATE();
2326 // free temporary surface
2327 SDL_FreeSurface(sdl_image_tmp);
2329 new_bitmap->width = new_bitmap->surface->w;
2330 new_bitmap->height = new_bitmap->surface->h;
2332 print_timestamp_done("SDLLoadImage");
2338 // ----------------------------------------------------------------------------
2339 // custom cursor fuctions
2340 // ----------------------------------------------------------------------------
2342 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2344 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2345 cursor_info->width, cursor_info->height,
2346 cursor_info->hot_x, cursor_info->hot_y);
2349 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2351 static struct MouseCursorInfo *last_cursor_info = NULL;
2352 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2353 static SDL_Cursor *cursor_default = NULL;
2354 static SDL_Cursor *cursor_current = NULL;
2356 // if invoked for the first time, store the SDL default cursor
2357 if (cursor_default == NULL)
2358 cursor_default = SDL_GetCursor();
2360 // only create new cursor if cursor info (custom only) has changed
2361 if (cursor_info != NULL && cursor_info != last_cursor_info)
2363 cursor_current = create_cursor(cursor_info);
2364 last_cursor_info = cursor_info;
2367 // only set new cursor if cursor info (custom or NULL) has changed
2368 if (cursor_info != last_cursor_info2)
2369 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2371 last_cursor_info2 = cursor_info;
2375 // ============================================================================
2377 // ============================================================================
2379 void SDLOpenAudio(void)
2381 if (program.headless)
2384 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2386 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2390 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2391 AUDIO_NUM_CHANNELS_STEREO,
2392 setup.system.audio_fragment_size) < 0)
2394 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2398 audio.sound_available = TRUE;
2399 audio.music_available = TRUE;
2400 audio.loops_available = TRUE;
2401 audio.sound_enabled = TRUE;
2403 // set number of available mixer channels
2404 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2405 audio.music_channel = MUSIC_CHANNEL;
2406 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2408 Mixer_InitChannels();
2411 void SDLCloseAudio(void)
2414 Mix_HaltChannel(-1);
2417 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2421 // ============================================================================
2423 // ============================================================================
2425 void SDLWaitEvent(Event *event)
2427 SDL_WaitEvent(event);
2430 void SDLCorrectRawMousePosition(int *x, int *y)
2432 if (sdl_renderer == NULL)
2435 // this corrects the raw mouse position for logical screen size within event
2436 // filters (correction done later by SDL library when handling mouse events)
2439 float scale_x, scale_y;
2441 SDL_RenderGetViewport(sdl_renderer, &viewport);
2442 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2444 *x = (int)(*x / scale_x);
2445 *y = (int)(*y / scale_y);
2452 // ============================================================================
2453 // joystick functions
2454 // ============================================================================
2456 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2457 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2458 static int sdl_js_axis[MAX_PLAYERS][2];
2459 static int sdl_js_button[MAX_PLAYERS][2];
2460 static boolean sdl_is_controller[MAX_PLAYERS];
2462 void SDLClearJoystickState(void)
2466 for (i = 0; i < MAX_PLAYERS; i++)
2468 for (j = 0; j < 2; j++)
2470 sdl_js_axis_raw[i][j] = -1;
2471 sdl_js_axis[i][j] = 0;
2472 sdl_js_button[i][j] = 0;
2477 boolean SDLOpenJoystick(int nr)
2479 if (nr < 0 || nr >= MAX_PLAYERS)
2482 sdl_is_controller[nr] = SDL_IsGameController(nr);
2485 Error(ERR_DEBUG, "opening joystick %d (%s)",
2486 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2489 if (sdl_is_controller[nr])
2490 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2492 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2494 return (sdl_joystick[nr] != NULL);
2497 void SDLCloseJoystick(int nr)
2499 if (nr < 0 || nr >= MAX_PLAYERS)
2503 Error(ERR_DEBUG, "closing joystick %d", nr);
2506 if (sdl_is_controller[nr])
2507 SDL_GameControllerClose(sdl_joystick[nr]);
2509 SDL_JoystickClose(sdl_joystick[nr]);
2511 sdl_joystick[nr] = NULL;
2514 boolean SDLCheckJoystickOpened(int nr)
2516 if (nr < 0 || nr >= MAX_PLAYERS)
2519 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2522 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2524 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2525 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2526 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2527 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2529 if (nr < 0 || nr >= MAX_PLAYERS)
2535 // prevent (slightly jittering, but centered) axis A from resetting axis B
2536 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2537 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2540 sdl_js_axis[nr][axis_id] = axis_value;
2541 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2544 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2546 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2547 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2548 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2549 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2550 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2551 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2552 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2553 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2556 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2557 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2558 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2559 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2560 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2561 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2562 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2563 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2565 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2566 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2567 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2568 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2569 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2571 if (nr < 0 || nr >= MAX_PLAYERS)
2574 if (button_id == -1)
2577 sdl_js_button[nr][button_id] = button_state;
2580 void HandleJoystickEvent(Event *event)
2582 // when using joystick, disable overlay touch buttons
2583 runtime.uses_touch_device = FALSE;
2585 switch (event->type)
2587 case SDL_CONTROLLERDEVICEADDED:
2589 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2590 event->cdevice.which);
2595 case SDL_CONTROLLERDEVICEREMOVED:
2597 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2598 event->cdevice.which);
2603 case SDL_CONTROLLERAXISMOTION:
2605 Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2606 event->caxis.which, event->caxis.axis, event->caxis.value);
2608 setJoystickAxis(event->caxis.which,
2610 event->caxis.value);
2613 case SDL_CONTROLLERBUTTONDOWN:
2615 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2616 event->cbutton.which, event->cbutton.button);
2618 setJoystickButton(event->cbutton.which,
2619 event->cbutton.button,
2623 case SDL_CONTROLLERBUTTONUP:
2625 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2626 event->cbutton.which, event->cbutton.button);
2628 setJoystickButton(event->cbutton.which,
2629 event->cbutton.button,
2633 case SDL_JOYAXISMOTION:
2634 if (sdl_is_controller[event->jaxis.which])
2638 Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2639 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2641 if (event->jaxis.axis < 4)
2642 setJoystickAxis(event->jaxis.which,
2644 event->jaxis.value);
2647 case SDL_JOYBUTTONDOWN:
2648 if (sdl_is_controller[event->jaxis.which])
2652 Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2653 event->jbutton.which, event->jbutton.button);
2655 if (event->jbutton.button < 4)
2656 setJoystickButton(event->jbutton.which,
2657 event->jbutton.button,
2661 case SDL_JOYBUTTONUP:
2662 if (sdl_is_controller[event->jaxis.which])
2666 Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2667 event->jbutton.which, event->jbutton.button);
2669 if (event->jbutton.button < 4)
2670 setJoystickButton(event->jbutton.which,
2671 event->jbutton.button,
2680 void SDLInitJoysticks(void)
2682 static boolean sdl_joystick_subsystem_initialized = FALSE;
2683 boolean print_warning = !sdl_joystick_subsystem_initialized;
2684 char *mappings_file_base = getPath2(options.conf_directory,
2685 GAMECONTROLLER_BASENAME);
2686 char *mappings_file_user = getPath2(getUserGameDataDir(),
2687 GAMECONTROLLER_BASENAME);
2691 if (!sdl_joystick_subsystem_initialized)
2693 sdl_joystick_subsystem_initialized = TRUE;
2695 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2697 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2699 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2703 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2705 // the included game controller base mappings should always be found
2706 if (num_mappings == -1)
2707 Error(ERR_WARN, "no game controller base mappings found");
2710 Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2713 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2716 // the personal game controller user mappings may or may not be found
2717 if (num_mappings == -1)
2718 Error(ERR_WARN, "no game controller user mappings found");
2720 Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2722 Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2725 checked_free(mappings_file_base);
2726 checked_free(mappings_file_user);
2729 for (i = 0; i < SDL_NumJoysticks(); i++)
2731 const char *name, *type;
2733 if (SDL_IsGameController(i))
2735 name = SDL_GameControllerNameForIndex(i);
2736 type = "game controller";
2740 name = SDL_JoystickNameForIndex(i);
2744 Error(ERR_INFO, "- joystick %d (%s): '%s'",
2745 i, type, (name ? name : "(Unknown)"));
2750 // assign joysticks from configured to connected joystick for all players
2751 for (i = 0; i < MAX_PLAYERS; i++)
2753 // get configured joystick for this player
2754 char *device_name = setup.input[i].joy.device_name;
2755 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2757 if (joystick_nr >= SDL_NumJoysticks())
2759 if (setup.input[i].use_joystick && print_warning)
2760 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2765 // store configured joystick number for each player
2766 joystick.nr[i] = joystick_nr;
2769 // now open all connected joysticks (regardless if configured or not)
2770 for (i = 0; i < SDL_NumJoysticks(); i++)
2772 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2773 if (SDLCheckJoystickOpened(i))
2774 SDLCloseJoystick(i);
2776 if (SDLOpenJoystick(i))
2777 joystick.status = JOYSTICK_ACTIVATED;
2778 else if (print_warning)
2779 Error(ERR_WARN, "cannot open joystick %d", i);
2782 SDLClearJoystickState();
2785 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2787 if (nr < 0 || nr >= MAX_PLAYERS)
2791 *x = sdl_js_axis[nr][0];
2793 *y = sdl_js_axis[nr][1];
2796 *b1 = sdl_js_button[nr][0];
2798 *b2 = sdl_js_button[nr][1];
2804 // ============================================================================
2805 // touch input overlay functions
2806 // ============================================================================
2808 #if defined(USE_TOUCH_INPUT_OVERLAY)
2809 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2812 int grid_xsize = overlay.grid_xsize;
2813 int grid_ysize = overlay.grid_ysize;
2816 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2817 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2819 for (x = 0; x < grid_xsize; x++)
2821 rect.x = (x + 0) * video.screen_width / grid_xsize;
2822 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2824 for (y = 0; y < grid_ysize; y++)
2826 rect.y = (y + 0) * video.screen_height / grid_ysize;
2827 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2829 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2830 SDL_RenderDrawRect(sdl_renderer, &rect);
2834 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2837 static void RenderFillRectangle(int x, int y, int width, int height)
2839 SDL_Rect rect = { x, y, width, height };
2841 SDL_RenderFillRect(sdl_renderer, &rect);
2844 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2846 static int alpha_direction = 0;
2847 static int alpha_highlight = 0;
2848 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2849 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2851 int grid_xsize = overlay.grid_xsize;
2852 int grid_ysize = overlay.grid_ysize;
2855 if (alpha == alpha_max)
2857 if (alpha_direction < 0)
2859 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2861 if (alpha_highlight == 0)
2862 alpha_direction = 1;
2866 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2868 if (alpha_highlight == alpha_max)
2869 alpha_direction = -1;
2874 alpha_direction = 1;
2875 alpha_highlight = alpha;
2878 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2880 for (x = 0; x < grid_xsize; x++)
2882 for (y = 0; y < grid_ysize; y++)
2884 int grid_button = overlay.grid_button[x][y];
2885 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2886 int alpha_draw = alpha;
2887 int outline_border = MV_NONE;
2888 int border_size = 2;
2889 boolean draw_outlined = setup.touch.draw_outlined;
2890 boolean draw_pressed = setup.touch.draw_pressed;
2892 if (grid_button == CHAR_GRID_BUTTON_NONE)
2895 if (grid_button == overlay.grid_button_highlight)
2897 draw_outlined = FALSE;
2898 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2901 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2904 draw_outlined = FALSE;
2906 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2909 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2911 rect.x = (x + 0) * video.screen_width / grid_xsize;
2912 rect.y = (y + 0) * video.screen_height / grid_ysize;
2913 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2914 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2916 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2918 rect.x += border_size;
2919 rect.w -= border_size;
2921 outline_border |= MV_LEFT;
2924 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2926 rect.w -= border_size;
2928 outline_border |= MV_RIGHT;
2931 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2933 rect.y += border_size;
2934 rect.h -= border_size;
2936 outline_border |= MV_UP;
2939 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2941 rect.h -= border_size;
2943 outline_border |= MV_DOWN;
2948 int rect_x = rect.x +
2949 (outline_border & MV_LEFT ? border_size : 0);
2950 int rect_w = rect.w -
2951 (outline_border & MV_LEFT ? border_size : 0) -
2952 (outline_border & MV_RIGHT ? border_size : 0);
2954 if (outline_border & MV_LEFT)
2955 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2957 if (outline_border & MV_RIGHT)
2958 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2959 border_size, rect.h);
2961 if (outline_border & MV_UP)
2962 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2964 if (outline_border & MV_DOWN)
2965 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
2966 rect_w, border_size);
2970 SDL_RenderFillRect(sdl_renderer, &rect);
2975 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2978 static void DrawTouchInputOverlay(void)
2980 static boolean deactivated = TRUE;
2981 static boolean show_grid = FALSE;
2982 static int alpha = 0;
2983 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2984 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2985 boolean active = (overlay.enabled && overlay.active);
2987 if (!active && deactivated)
2992 if (alpha < alpha_max)
2993 alpha = MIN(alpha + alpha_step, alpha_max);
2995 deactivated = FALSE;
2999 alpha = MAX(0, alpha - alpha_step);
3005 if (overlay.show_grid)
3007 else if (deactivated)
3011 DrawTouchInputOverlay_ShowGrid(alpha);
3013 DrawTouchInputOverlay_ShowGridButtons(alpha);
3016 static void DrawTouchGadgetsOverlay(void)
3018 DrawGadgets_OverlayTouchButtons();