1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
19 #define ENABLE_UNUSED_CODE 0 // currently unused functions
21 #define DEBUG_JOYSTICKS 0
24 // ============================================================================
26 // ============================================================================
28 // SDL internal variables
29 static SDL_Window *sdl_window = NULL;
30 static SDL_Renderer *sdl_renderer = NULL;
31 static SDL_Texture *sdl_texture_stream = NULL;
32 static SDL_Texture *sdl_texture_target = NULL;
33 static boolean fullscreen_enabled = FALSE;
34 static boolean limit_screen_updates = FALSE;
37 // functions from SGE library
38 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
40 #if defined(USE_TOUCH_INPUT_OVERLAY)
41 // functions to draw overlay graphics for touch device input
42 static void DrawTouchInputOverlay(void);
43 static void DrawTouchGadgetsOverlay(void);
46 void SDLLimitScreenUpdates(boolean enable)
48 limit_screen_updates = enable;
51 static void FinalizeScreen(int draw_target)
53 // copy global animations to render target buffer, if defined (below border)
54 if (gfx.draw_global_anim_function != NULL)
55 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
57 // copy global masked border to render target buffer, if defined
58 if (gfx.draw_global_border_function != NULL)
59 gfx.draw_global_border_function(draw_target);
61 // copy global animations to render target buffer, if defined (above border)
62 if (gfx.draw_global_anim_function != NULL)
63 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
65 // copy tile selection cursor to render target buffer, if defined (above all)
66 if (gfx.draw_tile_cursor_function != NULL)
67 gfx.draw_tile_cursor_function(draw_target);
70 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
72 static unsigned int update_screen_delay = 0;
73 unsigned int update_screen_delay_value = 50; // (milliseconds)
74 SDL_Surface *screen = backbuffer->surface;
76 if (limit_screen_updates &&
77 !DelayReached(&update_screen_delay, update_screen_delay_value))
80 LimitScreenUpdates(FALSE);
84 static int LastFrameCounter = 0;
85 boolean changed = (FrameCounter != LastFrameCounter);
87 printf("::: FrameCounter == %d [%s]\n", FrameCounter,
88 (changed ? "-" : "SAME FRAME UPDATED"));
90 LastFrameCounter = FrameCounter;
99 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
100 gfx.final_screen_bitmap != NULL) // may not be initialized yet
102 // draw global animations using bitmaps instead of using textures
103 // to prevent texture scaling artefacts (this is potentially slower)
105 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
106 gfx.win_xsize, gfx.win_ysize, 0, 0);
108 FinalizeScreen(DRAW_TO_SCREEN);
110 screen = gfx.final_screen_bitmap->surface;
112 // force full window redraw
116 SDL_Texture *sdl_texture = sdl_texture_stream;
118 // deactivate use of target texture if render targets are not supported
119 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
120 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
121 sdl_texture_target == NULL)
122 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
124 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
125 sdl_texture = sdl_texture_target;
129 int bytes_x = screen->pitch / video.width;
130 int bytes_y = screen->pitch;
132 SDL_UpdateTexture(sdl_texture, rect,
133 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
138 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
141 int xoff = video.screen_xoffset;
142 int yoff = video.screen_yoffset;
143 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
144 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
145 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
147 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
148 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
149 dst_rect2 = &dst_rect_screen;
151 dst_rect1 = &dst_rect_screen;
153 #if defined(HAS_SCREEN_KEYBOARD)
154 if (video.shifted_up || video.shifted_up_delay)
156 int time_current = SDL_GetTicks();
157 int pos = video.shifted_up_pos;
158 int pos_last = video.shifted_up_pos_last;
160 if (!DelayReachedExt(&video.shifted_up_delay, video.shifted_up_delay_value,
163 int delay = time_current - video.shifted_up_delay;
164 int delay_value = video.shifted_up_delay_value;
166 pos = pos_last + (pos - pos_last) * delay / delay_value;
170 video.shifted_up_pos_last = pos;
171 video.shifted_up_delay = 0;
174 SDL_Rect src_rect_up = { 0, pos, video.width, video.height - pos };
175 SDL_Rect dst_rect_up = { xoff, yoff, video.width, video.height - pos };
177 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
178 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
180 src_rect2 = &src_rect_up;
181 dst_rect2 = &dst_rect_up;
185 src_rect1 = &src_rect_up;
186 dst_rect1 = &dst_rect_up;
191 // clear render target buffer
192 SDL_RenderClear(sdl_renderer);
194 // set renderer to use target texture for rendering
195 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
196 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
197 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
199 // copy backbuffer texture to render target buffer
200 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
201 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
203 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
204 FinalizeScreen(DRAW_TO_SCREEN);
206 // when using target texture, copy it to screen buffer
207 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
208 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
210 SDL_SetRenderTarget(sdl_renderer, NULL);
211 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
214 #if defined(USE_TOUCH_INPUT_OVERLAY)
215 // draw overlay graphics for touch device input, if needed
216 DrawTouchInputOverlay();
218 // draw overlay gadgets for touch device input, if needed
219 DrawTouchGadgetsOverlay();
222 // global synchronization point of the game to align video frame delay
223 if (with_frame_delay)
224 WaitUntilDelayReached(&video.frame_delay, video.frame_delay_value);
226 video.frame_counter++;
228 // show render target buffer on screen
229 SDL_RenderPresent(sdl_renderer);
232 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
234 PumpEvents(); // execute event filter actions while waiting
236 UpdateScreenExt(rect, TRUE);
239 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
241 UpdateScreenExt(rect, FALSE);
244 void Delay_WithScreenUpdates(unsigned int delay)
246 unsigned int time_current = SDL_GetTicks();
247 unsigned int time_delayed = time_current + delay;
249 while (time_current < time_delayed)
251 // updating the screen contains waiting for frame delay (non-busy)
252 UpdateScreen_WithFrameDelay(NULL);
254 time_current = SDL_GetTicks();
258 static void SDLSetWindowIcon(char *basename)
260 // (setting the window icon on Mac OS X would replace the high-quality
261 // dock icon with the currently smaller (and uglier) icon from file)
263 #if !defined(PLATFORM_MACOSX)
264 char *filename = getCustomImageFilename(basename);
265 SDL_Surface *surface;
267 if (filename == NULL)
269 Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
274 if ((surface = IMG_Load(filename)) == NULL)
276 Error(ERR_WARN, "IMG_Load('%s') failed: %s", basename, SDL_GetError());
281 // set transparent color
282 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
283 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
285 SDL_SetWindowIcon(sdl_window, surface);
289 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
290 SDL_PixelFormat *format2)
292 return (format1->format == format2->format &&
293 format1->BitsPerPixel == format2->BitsPerPixel &&
294 format1->BytesPerPixel == format2->BytesPerPixel &&
295 format1->Rmask == format2->Rmask &&
296 format1->Gmask == format2->Gmask &&
297 format1->Bmask == format2->Bmask);
300 static Pixel SDLGetColorKey(SDL_Surface *surface)
304 if (SDL_GetColorKey(surface, &color_key) != 0)
310 static boolean SDLHasColorKey(SDL_Surface *surface)
312 return (SDLGetColorKey(surface) != -1);
315 static boolean SDLHasAlpha(SDL_Surface *surface)
317 SDL_BlendMode blend_mode;
319 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
322 return (blend_mode == SDL_BLENDMODE_BLEND);
325 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
327 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
329 SDL_SetSurfaceBlendMode(surface, blend_mode);
330 SDL_SetSurfaceAlphaMod(surface, alpha);
333 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
335 SDL_PixelFormat format;
336 SDL_Surface *new_surface;
341 if (backbuffer && backbuffer->surface)
343 format = *backbuffer->surface->format;
344 format.Amask = surface->format->Amask; // keep alpha channel
348 format = *surface->format;
351 new_surface = SDL_ConvertSurface(surface, &format, 0);
353 if (new_surface == NULL)
354 Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
356 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
357 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
358 SDL_SetColorKey(new_surface, SET_TRANSPARENT_PIXEL,
359 SDLGetColorKey(surface));
364 boolean SDLSetNativeSurface(SDL_Surface **surface)
366 SDL_Surface *new_surface;
368 if (surface == NULL ||
370 backbuffer == NULL ||
371 backbuffer->surface == NULL)
374 // if pixel format already optimized for destination surface, do nothing
375 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
378 new_surface = SDLGetNativeSurface(*surface);
380 SDL_FreeSurface(*surface);
382 *surface = new_surface;
387 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
389 if (program.headless)
392 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
395 Error(ERR_EXIT, "SDL_CreateTextureFromSurface() failed: %s",
401 void SDLCreateBitmapTextures(Bitmap *bitmap)
407 SDL_DestroyTexture(bitmap->texture);
408 if (bitmap->texture_masked)
409 SDL_DestroyTexture(bitmap->texture_masked);
411 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
412 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
415 void SDLFreeBitmapTextures(Bitmap *bitmap)
421 SDL_DestroyTexture(bitmap->texture);
422 if (bitmap->texture_masked)
423 SDL_DestroyTexture(bitmap->texture_masked);
425 bitmap->texture = NULL;
426 bitmap->texture_masked = NULL;
429 void SDLInitVideoDisplay(void)
431 // initialize SDL video
432 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
433 Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
435 // set default SDL depth
436 video.default_depth = 32; // (how to determine video depth in SDL2?)
438 // Code used with SDL 1.2:
439 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
442 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
444 if (program.headless)
447 video.window_scaling_percent = setup.window_scaling_percent;
448 video.window_scaling_quality = setup.window_scaling_quality;
450 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
452 // SDL 2.0: support for (desktop) fullscreen mode available
453 video.fullscreen_available = TRUE;
455 // open SDL video output device (window or fullscreen mode)
456 if (!SDLSetVideoMode(fullscreen))
457 Error(ERR_EXIT, "setting video mode failed");
459 // !!! SDL2 can only set the window icon if the window already exists !!!
461 SDLSetWindowIcon(program.icon_filename);
463 // set window and icon title
467 static void SDLInitVideoBuffer_DrawBuffer(void)
469 /* SDL cannot directly draw to the visible video framebuffer like X11,
470 but always uses a backbuffer, which is then blitted to the visible
471 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
472 visible video framebuffer with 'SDL_Flip', if the hardware supports
473 this). Therefore do not use an additional backbuffer for drawing, but
474 use a symbolic buffer (distinguishable from the SDL backbuffer) called
475 'window', which indicates that the SDL backbuffer should be updated to
476 the visible video framebuffer when attempting to blit to it.
478 For convenience, it seems to be a good idea to create this symbolic
479 buffer 'window' at the same size as the SDL backbuffer. Although it
480 should never be drawn to directly, it would do no harm nevertheless. */
482 // create additional (symbolic) buffer for double-buffering
483 ReCreateBitmap(&window, video.width, video.height);
485 // create dummy drawing buffer for headless mode, if needed
486 if (program.headless)
487 ReCreateBitmap(&backbuffer, video.width, video.height);
490 void SDLInitVideoBuffer(boolean fullscreen)
492 SDLInitVideoBuffer_VideoBuffer(fullscreen);
493 SDLInitVideoBuffer_DrawBuffer();
496 static boolean SDLCreateScreen(boolean fullscreen)
498 SDL_Surface *new_surface = NULL;
500 int surface_flags_window = SURFACE_FLAGS | SDL_WINDOW_RESIZABLE;
501 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
504 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
506 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
507 _without_ enabling 2D/3D acceleration and/or guest additions installed,
508 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
509 it will try to use accelerated graphics and apparently fails miserably) */
510 int renderer_flags = SDL_RENDERER_SOFTWARE;
513 SDLSetScreenSizeAndOffsets(video.width, video.height);
515 int width = video.width;
516 int height = video.height;
517 int screen_width = video.screen_width;
518 int screen_height = video.screen_height;
519 int surface_flags = (fullscreen ? surface_flags_fullscreen :
520 surface_flags_window);
522 // default window size is unscaled
523 video.window_width = screen_width;
524 video.window_height = screen_height;
526 // store if initial screen mode is fullscreen mode when changing screen size
527 video.fullscreen_initial = fullscreen;
529 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
531 video.window_width = window_scaling_factor * screen_width;
532 video.window_height = window_scaling_factor * screen_height;
534 if (sdl_texture_stream)
536 SDL_DestroyTexture(sdl_texture_stream);
537 sdl_texture_stream = NULL;
540 if (sdl_texture_target)
542 SDL_DestroyTexture(sdl_texture_target);
543 sdl_texture_target = NULL;
546 if (!(fullscreen && fullscreen_enabled))
550 SDL_DestroyRenderer(sdl_renderer);
556 SDL_DestroyWindow(sdl_window);
561 if (sdl_window == NULL)
562 sdl_window = SDL_CreateWindow(program.window_title,
563 SDL_WINDOWPOS_CENTERED,
564 SDL_WINDOWPOS_CENTERED,
569 if (sdl_window != NULL)
571 if (sdl_renderer == NULL)
572 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
574 if (sdl_renderer != NULL)
576 SDL_RenderSetLogicalSize(sdl_renderer, screen_width, screen_height);
577 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
578 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
580 SDLSetScreenVsyncMode(setup.vsync_mode);
582 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
583 SDL_PIXELFORMAT_ARGB8888,
584 SDL_TEXTUREACCESS_STREAMING,
587 if (SDL_RenderTargetSupported(sdl_renderer))
588 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
589 SDL_PIXELFORMAT_ARGB8888,
590 SDL_TEXTUREACCESS_TARGET,
593 if (sdl_texture_stream != NULL)
595 // use SDL default values for RGB masks and no alpha channel
596 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
598 if (new_surface == NULL)
599 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
603 Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
608 Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
613 Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
616 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
617 if (new_surface != NULL)
618 fullscreen_enabled = fullscreen;
620 if (backbuffer == NULL)
621 backbuffer = CreateBitmapStruct();
623 backbuffer->width = video.width;
624 backbuffer->height = video.height;
626 if (backbuffer->surface)
627 SDL_FreeSurface(backbuffer->surface);
629 backbuffer->surface = new_surface;
631 return (new_surface != NULL);
634 boolean SDLSetVideoMode(boolean fullscreen)
636 boolean success = FALSE;
640 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
642 // switch display to fullscreen mode, if available
643 success = SDLCreateScreen(TRUE);
647 // switching display to fullscreen mode failed -- do not try it again
648 video.fullscreen_available = FALSE;
652 video.fullscreen_enabled = TRUE;
656 if ((!fullscreen && video.fullscreen_enabled) || !success)
658 // switch display to window mode
659 success = SDLCreateScreen(FALSE);
663 // switching display to window mode failed -- should not happen
667 video.fullscreen_enabled = FALSE;
668 video.window_scaling_percent = setup.window_scaling_percent;
669 video.window_scaling_quality = setup.window_scaling_quality;
671 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
675 SDLRedrawWindow(); // map window
680 void SDLSetWindowTitle(void)
682 if (sdl_window == NULL)
685 SDL_SetWindowTitle(sdl_window, program.window_title);
688 void SDLSetWindowScaling(int window_scaling_percent)
690 if (sdl_window == NULL)
693 float window_scaling_factor = (float)window_scaling_percent / 100;
694 int new_window_width = (int)(window_scaling_factor * video.screen_width);
695 int new_window_height = (int)(window_scaling_factor * video.screen_height);
697 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
699 video.window_scaling_percent = window_scaling_percent;
700 video.window_width = new_window_width;
701 video.window_height = new_window_height;
706 void SDLSetWindowScalingQuality(char *window_scaling_quality)
708 SDL_Texture *new_texture;
710 if (sdl_texture_stream == NULL)
713 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
715 new_texture = SDL_CreateTexture(sdl_renderer,
716 SDL_PIXELFORMAT_ARGB8888,
717 SDL_TEXTUREACCESS_STREAMING,
718 video.width, video.height);
720 if (new_texture != NULL)
722 SDL_DestroyTexture(sdl_texture_stream);
724 sdl_texture_stream = new_texture;
727 if (SDL_RenderTargetSupported(sdl_renderer))
728 new_texture = SDL_CreateTexture(sdl_renderer,
729 SDL_PIXELFORMAT_ARGB8888,
730 SDL_TEXTUREACCESS_TARGET,
731 video.width, video.height);
735 if (new_texture != NULL)
737 SDL_DestroyTexture(sdl_texture_target);
739 sdl_texture_target = new_texture;
744 video.window_scaling_quality = window_scaling_quality;
747 void SDLSetWindowFullscreen(boolean fullscreen)
749 if (sdl_window == NULL)
752 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
754 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
755 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
757 // if screen size was changed in fullscreen mode, correct desktop window size
758 if (!fullscreen && video.fullscreen_initial)
760 SDLSetWindowScaling(setup.window_scaling_percent);
761 SDL_SetWindowPosition(sdl_window,
762 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
764 video.fullscreen_initial = FALSE;
768 void SDLSetDisplaySize(void)
770 SDL_Rect display_bounds;
772 SDL_GetDisplayBounds(0, &display_bounds);
774 video.display_width = display_bounds.w;
775 video.display_height = display_bounds.h;
778 Error(ERR_DEBUG, "SDL real screen size: %d x %d",
779 video.display_width, video.display_height);
783 void SDLSetScreenSizeAndOffsets(int width, int height)
785 // set default video screen size and offsets
786 video.screen_width = width;
787 video.screen_height = height;
788 video.screen_xoffset = 0;
789 video.screen_yoffset = 0;
791 #if defined(USE_COMPLETE_DISPLAY)
792 float ratio_video = (float) width / height;
793 float ratio_display = (float) video.display_width / video.display_height;
795 if (ratio_video != ratio_display)
797 // adjust drawable screen size to cover the whole device display
799 if (ratio_video < ratio_display)
800 video.screen_width *= ratio_display / ratio_video;
802 video.screen_height *= ratio_video / ratio_display;
804 video.screen_xoffset = (video.screen_width - width) / 2;
805 video.screen_yoffset = (video.screen_height - height) / 2;
808 Error(ERR_DEBUG, "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
810 video.screen_width, video.screen_height,
811 ratio_video, ratio_display);
817 void SDLSetScreenSizeForRenderer(int width, int height)
819 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
822 void SDLSetScreenProperties(void)
824 SDLSetScreenSizeAndOffsets(video.width, video.height);
825 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
828 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
830 video.screen_rendering_mode =
831 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
832 SPECIAL_RENDERING_BITMAP :
833 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
834 SPECIAL_RENDERING_TARGET:
835 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
836 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
839 void SDLSetScreenVsyncMode(char *vsync_mode)
842 (strEqual(vsync_mode, STR_VSYNC_MODE_NORMAL) ? VSYNC_MODE_NORMAL :
843 strEqual(vsync_mode, STR_VSYNC_MODE_ADAPTIVE) ? VSYNC_MODE_ADAPTIVE :
845 int result = SDL_GL_SetSwapInterval(interval);
847 // if adaptive vsync requested, but not supported, retry with normal vsync
848 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
849 SDL_GL_SetSwapInterval(VSYNC_MODE_NORMAL);
852 void SDLRedrawWindow(void)
854 UpdateScreen_WithoutFrameDelay(NULL);
857 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
860 if (program.headless)
863 SDL_Surface *surface =
864 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
867 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
869 SDLSetNativeSurface(&surface);
871 bitmap->surface = surface;
874 void SDLFreeBitmapPointers(Bitmap *bitmap)
877 SDL_FreeSurface(bitmap->surface);
878 if (bitmap->surface_masked)
879 SDL_FreeSurface(bitmap->surface_masked);
881 bitmap->surface = NULL;
882 bitmap->surface_masked = NULL;
885 SDL_DestroyTexture(bitmap->texture);
886 if (bitmap->texture_masked)
887 SDL_DestroyTexture(bitmap->texture_masked);
889 bitmap->texture = NULL;
890 bitmap->texture_masked = NULL;
893 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
894 int src_x, int src_y, int width, int height,
895 int dst_x, int dst_y, int mask_mode)
897 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
898 SDL_Rect src_rect, dst_rect;
910 // if (src_bitmap != backbuffer || dst_bitmap != window)
911 if (!(src_bitmap == backbuffer && dst_bitmap == window))
912 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
913 src_bitmap->surface_masked : src_bitmap->surface),
914 &src_rect, real_dst_bitmap->surface, &dst_rect);
916 if (dst_bitmap == window)
917 UpdateScreen_WithFrameDelay(&dst_rect);
920 void SDLBlitTexture(Bitmap *bitmap,
921 int src_x, int src_y, int width, int height,
922 int dst_x, int dst_y, int mask_mode)
924 SDL_Texture *texture;
929 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
944 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
947 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
950 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
958 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
960 if (dst_bitmap == window)
961 UpdateScreen_WithFrameDelay(&rect);
964 void PrepareFadeBitmap(int draw_target)
966 Bitmap *fade_bitmap =
967 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
968 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
970 if (fade_bitmap == NULL)
973 // copy backbuffer to fading buffer
974 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
976 // add border and animations to fading buffer
977 FinalizeScreen(draw_target);
980 void SDLFadeRectangle(int x, int y, int width, int height,
981 int fade_mode, int fade_delay, int post_delay,
982 void (*draw_border_function)(void))
984 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
985 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
986 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
987 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
988 SDL_Surface *surface_screen = backbuffer->surface;
989 SDL_Rect src_rect, dst_rect;
991 int src_x = x, src_y = y;
992 int dst_x = x, dst_y = y;
993 unsigned int time_last, time_current;
995 // store function for drawing global masked border
996 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
998 // deactivate drawing of global border while fading, if needed
999 if (draw_border_function == NULL)
1000 gfx.draw_global_border_function = NULL;
1005 src_rect.h = height;
1009 dst_rect.w = width; // (ignored)
1010 dst_rect.h = height; // (ignored)
1012 dst_rect2 = dst_rect;
1014 // before fading in, store backbuffer (without animation graphics)
1015 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1016 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1018 // copy source and target surfaces to temporary surfaces for fading
1019 if (fade_mode & FADE_TYPE_TRANSFORM)
1021 // (source and target fading buffer already prepared)
1023 else if (fade_mode & FADE_TYPE_FADE_IN)
1025 // (target fading buffer already prepared)
1026 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1028 else // FADE_TYPE_FADE_OUT
1030 // (source fading buffer already prepared)
1031 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1034 time_current = SDL_GetTicks();
1036 if (fade_mode == FADE_MODE_MELT)
1038 boolean done = FALSE;
1039 int melt_pixels = 2;
1040 int melt_columns = width / melt_pixels;
1041 int ypos[melt_columns];
1042 int max_steps = height / 8 + 32;
1047 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1049 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1051 ypos[0] = -GetSimpleRandom(16);
1053 for (i = 1 ; i < melt_columns; i++)
1055 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1057 ypos[i] = ypos[i - 1] + r;
1070 time_last = time_current;
1071 time_current = SDL_GetTicks();
1072 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1073 steps_final = MIN(MAX(0, steps), max_steps);
1077 done = (steps_done >= steps_final);
1079 for (i = 0 ; i < melt_columns; i++)
1087 else if (ypos[i] < height)
1092 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1094 if (ypos[i] + dy >= height)
1095 dy = height - ypos[i];
1097 // copy part of (appearing) target surface to upper area
1098 src_rect.x = src_x + i * melt_pixels;
1099 // src_rect.y = src_y + ypos[i];
1101 src_rect.w = melt_pixels;
1103 src_rect.h = ypos[i] + dy;
1105 dst_rect.x = dst_x + i * melt_pixels;
1106 // dst_rect.y = dst_y + ypos[i];
1109 if (steps_done >= steps_final)
1110 SDL_BlitSurface(surface_target, &src_rect,
1111 surface_screen, &dst_rect);
1115 // copy part of (disappearing) source surface to lower area
1116 src_rect.x = src_x + i * melt_pixels;
1118 src_rect.w = melt_pixels;
1119 src_rect.h = height - ypos[i];
1121 dst_rect.x = dst_x + i * melt_pixels;
1122 dst_rect.y = dst_y + ypos[i];
1124 if (steps_done >= steps_final)
1125 SDL_BlitSurface(surface_source, &src_rect,
1126 surface_screen, &dst_rect);
1132 src_rect.x = src_x + i * melt_pixels;
1134 src_rect.w = melt_pixels;
1135 src_rect.h = height;
1137 dst_rect.x = dst_x + i * melt_pixels;
1140 if (steps_done >= steps_final)
1141 SDL_BlitSurface(surface_target, &src_rect,
1142 surface_screen, &dst_rect);
1146 if (steps_done >= steps_final)
1148 if (draw_border_function != NULL)
1149 draw_border_function();
1151 UpdateScreen_WithFrameDelay(&dst_rect2);
1155 else if (fade_mode == FADE_MODE_CURTAIN)
1159 int xx_size = width / 2;
1161 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1163 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1165 for (xx = 0; xx < xx_size;)
1167 time_last = time_current;
1168 time_current = SDL_GetTicks();
1169 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1170 xx_final = MIN(MAX(0, xx), xx_size);
1175 src_rect.h = height;
1180 // draw new (target) image to screen buffer
1181 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1183 if (xx_final < xx_size)
1185 src_rect.w = xx_size - xx_final;
1186 src_rect.h = height;
1188 // draw old (source) image to screen buffer (left side)
1190 src_rect.x = src_x + xx_final;
1193 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1195 // draw old (source) image to screen buffer (right side)
1197 src_rect.x = src_x + xx_size;
1198 dst_rect.x = dst_x + xx_size + xx_final;
1200 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1203 if (draw_border_function != NULL)
1204 draw_border_function();
1206 // only update the region of the screen that is affected from fading
1207 UpdateScreen_WithFrameDelay(&dst_rect2);
1210 else // fading in, fading out or cross-fading
1215 for (alpha = 0.0; alpha < 255.0;)
1217 time_last = time_current;
1218 time_current = SDL_GetTicks();
1219 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1220 alpha_final = MIN(MAX(0, alpha), 255);
1222 // draw existing (source) image to screen buffer
1223 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1225 // draw new (target) image to screen buffer using alpha blending
1226 SDLSetAlpha(surface_target, TRUE, alpha_final);
1227 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1229 if (draw_border_function != NULL)
1230 draw_border_function();
1232 // only update the region of the screen that is affected from fading
1233 UpdateScreen_WithFrameDelay(&dst_rect);
1238 Delay_WithScreenUpdates(post_delay);
1240 // restore function for drawing global masked border
1241 gfx.draw_global_border_function = draw_global_border_function;
1243 // after fading in, restore backbuffer (without animation graphics)
1244 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1245 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1248 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1249 int to_x, int to_y, Uint32 color)
1251 SDL_Surface *surface = dst_bitmap->surface;
1255 swap_numbers(&from_x, &to_x);
1258 swap_numbers(&from_y, &to_y);
1262 rect.w = (to_x - from_x + 1);
1263 rect.h = (to_y - from_y + 1);
1265 SDL_FillRect(surface, &rect, color);
1268 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1269 int to_x, int to_y, Uint32 color)
1271 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1274 #if ENABLE_UNUSED_CODE
1275 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1276 int num_points, Uint32 color)
1281 for (i = 0; i < num_points - 1; i++)
1283 for (x = 0; x < line_width; x++)
1285 for (y = 0; y < line_width; y++)
1287 int dx = x - line_width / 2;
1288 int dy = y - line_width / 2;
1290 if ((x == 0 && y == 0) ||
1291 (x == 0 && y == line_width - 1) ||
1292 (x == line_width - 1 && y == 0) ||
1293 (x == line_width - 1 && y == line_width - 1))
1296 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1297 points[i+1].x + dx, points[i+1].y + dy, color);
1304 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1306 SDL_Surface *surface = src_bitmap->surface;
1308 switch (surface->format->BytesPerPixel)
1310 case 1: // assuming 8-bpp
1312 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1316 case 2: // probably 15-bpp or 16-bpp
1318 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1322 case 3: // slow 24-bpp mode; usually not used
1325 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1329 shift = surface->format->Rshift;
1330 color |= *(pix + shift / 8) >> shift;
1331 shift = surface->format->Gshift;
1332 color |= *(pix + shift / 8) >> shift;
1333 shift = surface->format->Bshift;
1334 color |= *(pix + shift / 8) >> shift;
1340 case 4: // probably 32-bpp
1342 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1351 // ============================================================================
1352 // The following functions were taken from the SGE library
1353 // (SDL Graphics Extension Library) by Anders Lindström
1354 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1355 // ============================================================================
1357 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1359 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1361 switch (surface->format->BytesPerPixel)
1366 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1372 // Probably 15-bpp or 16-bpp
1373 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1379 // Slow 24-bpp mode, usually not used
1383 // Gack - slow, but endian correct
1384 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1385 shift = surface->format->Rshift;
1386 *(pix+shift/8) = color>>shift;
1387 shift = surface->format->Gshift;
1388 *(pix+shift/8) = color>>shift;
1389 shift = surface->format->Bshift;
1390 *(pix+shift/8) = color>>shift;
1397 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1405 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1406 Uint8 R, Uint8 G, Uint8 B)
1408 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1411 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1413 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1416 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1418 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1421 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1426 // Gack - slow, but endian correct
1427 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1428 shift = surface->format->Rshift;
1429 *(pix+shift/8) = color>>shift;
1430 shift = surface->format->Gshift;
1431 *(pix+shift/8) = color>>shift;
1432 shift = surface->format->Bshift;
1433 *(pix+shift/8) = color>>shift;
1436 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1438 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1441 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1443 switch (dest->format->BytesPerPixel)
1446 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1450 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1454 _PutPixel24(dest,x,y,color);
1458 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1464 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1466 if (SDL_MUSTLOCK(surface))
1468 if (SDL_LockSurface(surface) < 0)
1474 _PutPixel(surface, x, y, color);
1476 if (SDL_MUSTLOCK(surface))
1478 SDL_UnlockSurface(surface);
1483 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1484 Uint8 r, Uint8 g, Uint8 b)
1486 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1489 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1491 if (y >= 0 && y <= dest->h - 1)
1493 switch (dest->format->BytesPerPixel)
1496 return y*dest->pitch;
1500 return y*dest->pitch/2;
1504 return y*dest->pitch;
1508 return y*dest->pitch/4;
1516 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1519 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1521 switch (surface->format->BytesPerPixel)
1526 *((Uint8 *)surface->pixels + ypitch + x) = color;
1532 // Probably 15-bpp or 16-bpp
1533 *((Uint16 *)surface->pixels + ypitch + x) = color;
1539 // Slow 24-bpp mode, usually not used
1543 // Gack - slow, but endian correct
1544 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1545 shift = surface->format->Rshift;
1546 *(pix+shift/8) = color>>shift;
1547 shift = surface->format->Gshift;
1548 *(pix+shift/8) = color>>shift;
1549 shift = surface->format->Bshift;
1550 *(pix+shift/8) = color>>shift;
1557 *((Uint32 *)surface->pixels + ypitch + x) = color;
1564 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1569 if (SDL_MUSTLOCK(Surface))
1571 if (SDL_LockSurface(Surface) < 0)
1585 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1589 if (x2 > Surface->w - 1)
1590 x2 = Surface->w - 1;
1597 SDL_FillRect(Surface, &l, Color);
1599 if (SDL_MUSTLOCK(Surface))
1601 SDL_UnlockSurface(Surface);
1605 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1606 Uint8 R, Uint8 G, Uint8 B)
1608 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1611 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1624 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1628 if (x2 > Surface->w - 1)
1629 x2 = Surface->w - 1;
1636 SDL_FillRect(Surface, &l, Color);
1639 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1644 if (SDL_MUSTLOCK(Surface))
1646 if (SDL_LockSurface(Surface) < 0)
1660 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1664 if (y2 > Surface->h - 1)
1665 y2 = Surface->h - 1;
1672 SDL_FillRect(Surface, &l, Color);
1674 if (SDL_MUSTLOCK(Surface))
1676 SDL_UnlockSurface(Surface);
1680 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1681 Uint8 R, Uint8 G, Uint8 B)
1683 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1686 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1699 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1703 if (y2 > Surface->h - 1)
1704 y2 = Surface->h - 1;
1711 SDL_FillRect(Surface, &l, Color);
1715 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1716 Sint16 x2, Sint16 y2, Uint32 Color,
1717 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1720 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1725 sdx = (dx < 0) ? -1 : 1;
1726 sdy = (dy < 0) ? -1 : 1;
1738 for (x = 0; x < dx; x++)
1740 Callback(Surface, px, py, Color);
1754 for (y = 0; y < dy; y++)
1756 Callback(Surface, px, py, Color);
1771 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1772 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1773 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1776 sge_DoLine(Surface, X1, Y1, X2, Y2,
1777 SDL_MapRGB(Surface->format, R, G, B), Callback);
1781 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1784 if (SDL_MUSTLOCK(Surface))
1786 if (SDL_LockSurface(Surface) < 0)
1791 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1793 // unlock the display
1794 if (SDL_MUSTLOCK(Surface))
1796 SDL_UnlockSurface(Surface);
1801 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1802 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1804 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1808 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1810 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1814 // ----------------------------------------------------------------------------
1815 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1816 // ----------------------------------------------------------------------------
1818 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1819 int width, int height, Uint32 color)
1823 for (y = src_y; y < src_y + height; y++)
1825 for (x = src_x; x < src_x + width; x++)
1827 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1829 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1834 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1835 int src_x, int src_y, int width, int height,
1836 int dst_x, int dst_y)
1840 for (y = 0; y < height; y++)
1842 for (x = 0; x < width; x++)
1844 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1846 if (pixel != BLACK_PIXEL)
1847 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1853 // ============================================================================
1854 // The following functions were taken from the SDL_gfx library version 2.0.3
1855 // (Rotozoomer) by Andreas Schiffler
1856 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1857 // ============================================================================
1859 // ----------------------------------------------------------------------------
1862 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1863 // ----------------------------------------------------------------------------
1873 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1876 tColorRGBA *sp, *csp, *dp;
1880 sp = csp = (tColorRGBA *) src->pixels;
1881 dp = (tColorRGBA *) dst->pixels;
1882 dgap = dst->pitch - dst->w * 4;
1884 for (y = 0; y < dst->h; y++)
1888 for (x = 0; x < dst->w; x++)
1890 tColorRGBA *sp0 = sp;
1891 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1892 tColorRGBA *sp00 = &sp0[0];
1893 tColorRGBA *sp01 = &sp0[1];
1894 tColorRGBA *sp10 = &sp1[0];
1895 tColorRGBA *sp11 = &sp1[1];
1898 // create new color pixel from all four source color pixels
1899 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1900 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1901 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1902 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1907 // advance source pointers
1910 // advance destination pointer
1914 // advance source pointer
1915 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1917 // advance destination pointers
1918 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1924 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1926 int x, y, *sax, *say, *csax, *csay;
1928 tColorRGBA *sp, *csp, *csp0, *dp;
1931 // use specialized zoom function when scaling down to exactly half size
1932 if (src->w == 2 * dst->w &&
1933 src->h == 2 * dst->h)
1934 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1937 sx = (float) src->w / (float) dst->w;
1938 sy = (float) src->h / (float) dst->h;
1940 // allocate memory for row increments
1941 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1942 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1944 // precalculate row increments
1945 for (x = 0; x <= dst->w; x++)
1946 *csax++ = (int)(sx * x);
1948 for (y = 0; y <= dst->h; y++)
1949 *csay++ = (int)(sy * y);
1952 sp = csp = csp0 = (tColorRGBA *) src->pixels;
1953 dp = (tColorRGBA *) dst->pixels;
1954 dgap = dst->pitch - dst->w * 4;
1957 for (y = 0; y < dst->h; y++)
1962 for (x = 0; x < dst->w; x++)
1967 // advance source pointers
1971 // advance destination pointer
1975 // advance source pointer
1977 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
1979 // advance destination pointers
1980 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1989 // ----------------------------------------------------------------------------
1992 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
1993 // ----------------------------------------------------------------------------
1995 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
1997 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1998 Uint8 *sp, *dp, *csp;
2002 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2003 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2005 // allocate memory for row increments
2006 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2007 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2009 // precalculate row increments
2012 for (x = 0; x < dst->w; x++)
2015 *csax = (csx >> 16);
2022 for (y = 0; y < dst->h; y++)
2025 *csay = (csy >> 16);
2032 for (x = 0; x < dst->w; x++)
2040 for (y = 0; y < dst->h; y++)
2047 sp = csp = (Uint8 *) src->pixels;
2048 dp = (Uint8 *) dst->pixels;
2049 dgap = dst->pitch - dst->w;
2053 for (y = 0; y < dst->h; y++)
2057 for (x = 0; x < dst->w; x++)
2062 // advance source pointers
2066 // advance destination pointer
2070 // advance source pointer (for row)
2071 csp += ((*csay) * src->pitch);
2074 // advance destination pointers
2084 // ----------------------------------------------------------------------------
2087 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2088 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2089 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2090 // into a 32bit RGBA format on the fly.
2091 // ----------------------------------------------------------------------------
2093 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2095 SDL_Surface *zoom_src = NULL;
2096 SDL_Surface *zoom_dst = NULL;
2097 boolean is_converted = FALSE;
2104 // determine if source surface is 32 bit or 8 bit
2105 is_32bit = (src->format->BitsPerPixel == 32);
2107 if (is_32bit || src->format->BitsPerPixel == 8)
2109 // use source surface 'as is'
2114 // new source surface is 32 bit with a defined RGB ordering
2115 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2116 0x000000ff, 0x0000ff00, 0x00ff0000,
2117 (src->format->Amask ? 0xff000000 : 0));
2118 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2120 is_converted = TRUE;
2123 // allocate surface to completely contain the zoomed surface
2126 // target surface is 32 bit with source RGBA/ABGR ordering
2127 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2128 zoom_src->format->Rmask,
2129 zoom_src->format->Gmask,
2130 zoom_src->format->Bmask,
2131 zoom_src->format->Amask);
2135 // target surface is 8 bit
2136 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2140 // lock source surface
2141 SDL_LockSurface(zoom_src);
2143 // check which kind of surface we have
2146 // call the 32 bit transformation routine to do the zooming
2147 zoomSurfaceRGBA(zoom_src, zoom_dst);
2152 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2153 zoom_dst->format->palette->colors[i] =
2154 zoom_src->format->palette->colors[i];
2155 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2157 // call the 8 bit transformation routine to do the zooming
2158 zoomSurfaceY(zoom_src, zoom_dst);
2161 // unlock source surface
2162 SDL_UnlockSurface(zoom_src);
2164 // free temporary surface
2166 SDL_FreeSurface(zoom_src);
2168 // return destination surface
2172 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2174 SDL_Surface *new_surface;
2176 if (surface == NULL)
2179 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2180 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2182 // remove alpha channel from native non-transparent surface, if defined
2183 SDLSetAlpha(new_surface, FALSE, 0);
2185 // remove transparent color from native non-transparent surface, if defined
2186 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2191 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2193 Bitmap *dst_bitmap = CreateBitmapStruct();
2194 SDL_Surface *src_surface = src_bitmap->surface_masked;
2195 SDL_Surface *dst_surface;
2197 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2198 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2200 dst_bitmap->width = dst_width;
2201 dst_bitmap->height = dst_height;
2203 // create zoomed temporary surface from source surface
2204 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2206 // create native format destination surface from zoomed temporary surface
2207 SDLSetNativeSurface(&dst_surface);
2209 // set color key for zoomed surface from source surface, if defined
2210 if (SDLHasColorKey(src_surface))
2211 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2212 SDLGetColorKey(src_surface));
2214 // create native non-transparent surface for opaque blitting
2215 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2217 // set native transparent surface for masked blitting
2218 dst_bitmap->surface_masked = dst_surface;
2224 // ============================================================================
2225 // load image to bitmap
2226 // ============================================================================
2228 Bitmap *SDLLoadImage(char *filename)
2230 Bitmap *new_bitmap = CreateBitmapStruct();
2231 SDL_Surface *sdl_image_tmp;
2233 if (program.headless)
2235 // prevent sanity check warnings at later stage
2236 new_bitmap->width = new_bitmap->height = 1;
2241 print_timestamp_init("SDLLoadImage");
2243 print_timestamp_time(getBaseNamePtr(filename));
2245 // load image to temporary surface
2246 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2247 Error(ERR_EXIT, "IMG_Load('%s') failed: %s", getBaseNamePtr(filename),
2250 print_timestamp_time("IMG_Load");
2252 UPDATE_BUSY_STATE();
2254 // create native non-transparent surface for current image
2255 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2256 Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2258 print_timestamp_time("SDLGetNativeSurface (opaque)");
2260 UPDATE_BUSY_STATE();
2262 // set black pixel to transparent if no alpha channel / transparent color
2263 if (!SDLHasAlpha(sdl_image_tmp) &&
2264 !SDLHasColorKey(sdl_image_tmp))
2265 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2266 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2268 // create native transparent surface for current image
2269 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2270 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2272 print_timestamp_time("SDLGetNativeSurface (masked)");
2274 UPDATE_BUSY_STATE();
2276 // free temporary surface
2277 SDL_FreeSurface(sdl_image_tmp);
2279 new_bitmap->width = new_bitmap->surface->w;
2280 new_bitmap->height = new_bitmap->surface->h;
2282 print_timestamp_done("SDLLoadImage");
2288 // ----------------------------------------------------------------------------
2289 // custom cursor fuctions
2290 // ----------------------------------------------------------------------------
2292 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2294 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2295 cursor_info->width, cursor_info->height,
2296 cursor_info->hot_x, cursor_info->hot_y);
2299 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2301 static struct MouseCursorInfo *last_cursor_info = NULL;
2302 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2303 static SDL_Cursor *cursor_default = NULL;
2304 static SDL_Cursor *cursor_current = NULL;
2306 // if invoked for the first time, store the SDL default cursor
2307 if (cursor_default == NULL)
2308 cursor_default = SDL_GetCursor();
2310 // only create new cursor if cursor info (custom only) has changed
2311 if (cursor_info != NULL && cursor_info != last_cursor_info)
2313 cursor_current = create_cursor(cursor_info);
2314 last_cursor_info = cursor_info;
2317 // only set new cursor if cursor info (custom or NULL) has changed
2318 if (cursor_info != last_cursor_info2)
2319 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2321 last_cursor_info2 = cursor_info;
2325 // ============================================================================
2327 // ============================================================================
2329 void SDLOpenAudio(void)
2331 if (program.headless)
2334 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2336 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2340 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2341 AUDIO_NUM_CHANNELS_STEREO,
2342 setup.system.audio_fragment_size) < 0)
2344 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2348 audio.sound_available = TRUE;
2349 audio.music_available = TRUE;
2350 audio.loops_available = TRUE;
2351 audio.sound_enabled = TRUE;
2353 // set number of available mixer channels
2354 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2355 audio.music_channel = MUSIC_CHANNEL;
2356 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2358 Mixer_InitChannels();
2361 void SDLCloseAudio(void)
2364 Mix_HaltChannel(-1);
2367 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2371 // ============================================================================
2373 // ============================================================================
2375 void SDLWaitEvent(Event *event)
2377 SDL_WaitEvent(event);
2380 void SDLCorrectRawMousePosition(int *x, int *y)
2382 if (sdl_renderer == NULL)
2385 // this corrects the raw mouse position for logical screen size within event
2386 // filters (correction done later by SDL library when handling mouse events)
2389 float scale_x, scale_y;
2391 SDL_RenderGetViewport(sdl_renderer, &viewport);
2392 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2394 *x = (int)(*x / scale_x);
2395 *y = (int)(*y / scale_y);
2402 // ============================================================================
2403 // joystick functions
2404 // ============================================================================
2406 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2407 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2408 static int sdl_js_axis[MAX_PLAYERS][2];
2409 static int sdl_js_button[MAX_PLAYERS][2];
2410 static boolean sdl_is_controller[MAX_PLAYERS];
2412 void SDLClearJoystickState(void)
2416 for (i = 0; i < MAX_PLAYERS; i++)
2418 for (j = 0; j < 2; j++)
2420 sdl_js_axis_raw[i][j] = -1;
2421 sdl_js_axis[i][j] = 0;
2422 sdl_js_button[i][j] = 0;
2427 boolean SDLOpenJoystick(int nr)
2429 if (nr < 0 || nr >= MAX_PLAYERS)
2432 sdl_is_controller[nr] = SDL_IsGameController(nr);
2435 Error(ERR_DEBUG, "opening joystick %d (%s)",
2436 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2439 if (sdl_is_controller[nr])
2440 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2442 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2444 return (sdl_joystick[nr] != NULL);
2447 void SDLCloseJoystick(int nr)
2449 if (nr < 0 || nr >= MAX_PLAYERS)
2453 Error(ERR_DEBUG, "closing joystick %d", nr);
2456 if (sdl_is_controller[nr])
2457 SDL_GameControllerClose(sdl_joystick[nr]);
2459 SDL_JoystickClose(sdl_joystick[nr]);
2461 sdl_joystick[nr] = NULL;
2464 boolean SDLCheckJoystickOpened(int nr)
2466 if (nr < 0 || nr >= MAX_PLAYERS)
2469 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2472 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2474 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2475 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2476 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2477 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2479 if (nr < 0 || nr >= MAX_PLAYERS)
2485 // prevent (slightly jittering, but centered) axis A from resetting axis B
2486 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2487 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2490 sdl_js_axis[nr][axis_id] = axis_value;
2491 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2494 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2496 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2497 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2498 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2499 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2500 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2501 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2502 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2503 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2506 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2507 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2508 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2509 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2510 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2511 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2512 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2513 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2515 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2516 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2517 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2518 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2519 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2521 if (nr < 0 || nr >= MAX_PLAYERS)
2524 if (button_id == -1)
2527 sdl_js_button[nr][button_id] = button_state;
2530 void HandleJoystickEvent(Event *event)
2534 case SDL_CONTROLLERDEVICEADDED:
2536 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2537 event->cdevice.which);
2542 case SDL_CONTROLLERDEVICEREMOVED:
2544 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2545 event->cdevice.which);
2550 case SDL_CONTROLLERAXISMOTION:
2552 Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2553 event->caxis.which, event->caxis.axis, event->caxis.value);
2555 setJoystickAxis(event->caxis.which,
2557 event->caxis.value);
2560 case SDL_CONTROLLERBUTTONDOWN:
2562 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2563 event->cbutton.which, event->cbutton.button);
2565 setJoystickButton(event->cbutton.which,
2566 event->cbutton.button,
2570 case SDL_CONTROLLERBUTTONUP:
2572 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2573 event->cbutton.which, event->cbutton.button);
2575 setJoystickButton(event->cbutton.which,
2576 event->cbutton.button,
2580 case SDL_JOYAXISMOTION:
2581 if (sdl_is_controller[event->jaxis.which])
2585 Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2586 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2588 if (event->jaxis.axis < 4)
2589 setJoystickAxis(event->jaxis.which,
2591 event->jaxis.value);
2594 case SDL_JOYBUTTONDOWN:
2595 if (sdl_is_controller[event->jaxis.which])
2599 Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2600 event->jbutton.which, event->jbutton.button);
2602 if (event->jbutton.button < 4)
2603 setJoystickButton(event->jbutton.which,
2604 event->jbutton.button,
2608 case SDL_JOYBUTTONUP:
2609 if (sdl_is_controller[event->jaxis.which])
2613 Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2614 event->jbutton.which, event->jbutton.button);
2616 if (event->jbutton.button < 4)
2617 setJoystickButton(event->jbutton.which,
2618 event->jbutton.button,
2627 void SDLInitJoysticks(void)
2629 static boolean sdl_joystick_subsystem_initialized = FALSE;
2630 boolean print_warning = !sdl_joystick_subsystem_initialized;
2631 char *mappings_file_base = getPath2(options.conf_directory,
2632 GAMECONTROLLER_BASENAME);
2633 char *mappings_file_user = getPath2(getUserGameDataDir(),
2634 GAMECONTROLLER_BASENAME);
2638 if (!sdl_joystick_subsystem_initialized)
2640 sdl_joystick_subsystem_initialized = TRUE;
2642 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2644 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2646 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2650 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2652 // the included game controller base mappings should always be found
2653 if (num_mappings == -1)
2654 Error(ERR_WARN, "no game controller base mappings found");
2657 Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2660 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2663 // the personal game controller user mappings may or may not be found
2664 if (num_mappings == -1)
2665 Error(ERR_WARN, "no game controller user mappings found");
2667 Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2669 Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2672 checked_free(mappings_file_base);
2673 checked_free(mappings_file_user);
2676 for (i = 0; i < SDL_NumJoysticks(); i++)
2678 const char *name, *type;
2680 if (SDL_IsGameController(i))
2682 name = SDL_GameControllerNameForIndex(i);
2683 type = "game controller";
2687 name = SDL_JoystickNameForIndex(i);
2691 Error(ERR_INFO, "- joystick %d (%s): '%s'",
2692 i, type, (name ? name : "(Unknown)"));
2697 // assign joysticks from configured to connected joystick for all players
2698 for (i = 0; i < MAX_PLAYERS; i++)
2700 // get configured joystick for this player
2701 char *device_name = setup.input[i].joy.device_name;
2702 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2704 if (joystick_nr >= SDL_NumJoysticks())
2706 if (setup.input[i].use_joystick && print_warning)
2707 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2712 // store configured joystick number for each player
2713 joystick.nr[i] = joystick_nr;
2716 // now open all connected joysticks (regardless if configured or not)
2717 for (i = 0; i < SDL_NumJoysticks(); i++)
2719 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2720 if (SDLCheckJoystickOpened(i))
2721 SDLCloseJoystick(i);
2723 if (SDLOpenJoystick(i))
2724 joystick.status = JOYSTICK_ACTIVATED;
2725 else if (print_warning)
2726 Error(ERR_WARN, "cannot open joystick %d", i);
2729 SDLClearJoystickState();
2732 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2734 if (nr < 0 || nr >= MAX_PLAYERS)
2738 *x = sdl_js_axis[nr][0];
2740 *y = sdl_js_axis[nr][1];
2743 *b1 = sdl_js_button[nr][0];
2745 *b2 = sdl_js_button[nr][1];
2751 // ============================================================================
2752 // touch input overlay functions
2753 // ============================================================================
2755 #if defined(USE_TOUCH_INPUT_OVERLAY)
2756 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2759 int grid_xsize = overlay.grid_xsize;
2760 int grid_ysize = overlay.grid_ysize;
2763 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2764 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2766 for (x = 0; x < grid_xsize; x++)
2768 rect.x = (x + 0) * video.screen_width / grid_xsize;
2769 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2771 for (y = 0; y < grid_ysize; y++)
2773 rect.y = (y + 0) * video.screen_height / grid_ysize;
2774 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2776 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2777 SDL_RenderDrawRect(sdl_renderer, &rect);
2781 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2784 static void RenderFillRectangle(int x, int y, int width, int height)
2786 SDL_Rect rect = { x, y, width, height };
2788 SDL_RenderFillRect(sdl_renderer, &rect);
2791 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2793 static int alpha_direction = 0;
2794 static int alpha_highlight = 0;
2795 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2796 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2798 int grid_xsize = overlay.grid_xsize;
2799 int grid_ysize = overlay.grid_ysize;
2802 if (alpha == alpha_max)
2804 if (alpha_direction < 0)
2806 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2808 if (alpha_highlight == 0)
2809 alpha_direction = 1;
2813 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2815 if (alpha_highlight == alpha_max)
2816 alpha_direction = -1;
2821 alpha_direction = 1;
2822 alpha_highlight = alpha;
2825 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2827 for (x = 0; x < grid_xsize; x++)
2829 for (y = 0; y < grid_ysize; y++)
2831 int grid_button = overlay.grid_button[x][y];
2832 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2833 int alpha_draw = alpha;
2834 int outline_border = MV_NONE;
2835 int border_size = 2;
2836 boolean draw_outlined = setup.touch.draw_outlined;
2837 boolean draw_pressed = setup.touch.draw_pressed;
2839 if (grid_button == CHAR_GRID_BUTTON_NONE)
2842 if (grid_button == overlay.grid_button_highlight)
2844 draw_outlined = FALSE;
2845 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2848 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2851 draw_outlined = FALSE;
2853 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2856 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2858 rect.x = (x + 0) * video.screen_width / grid_xsize;
2859 rect.y = (y + 0) * video.screen_height / grid_ysize;
2860 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2861 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2863 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2865 rect.x += border_size;
2866 rect.w -= border_size;
2868 outline_border |= MV_LEFT;
2871 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2873 rect.w -= border_size;
2875 outline_border |= MV_RIGHT;
2878 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2880 rect.y += border_size;
2881 rect.h -= border_size;
2883 outline_border |= MV_UP;
2886 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2888 rect.h -= border_size;
2890 outline_border |= MV_DOWN;
2895 int rect_x = rect.x +
2896 (outline_border & MV_LEFT ? border_size : 0);
2897 int rect_w = rect.w -
2898 (outline_border & MV_LEFT ? border_size : 0) -
2899 (outline_border & MV_RIGHT ? border_size : 0);
2901 if (outline_border & MV_LEFT)
2902 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2904 if (outline_border & MV_RIGHT)
2905 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2906 border_size, rect.h);
2908 if (outline_border & MV_UP)
2909 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2911 if (outline_border & MV_DOWN)
2912 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
2913 rect_w, border_size);
2917 SDL_RenderFillRect(sdl_renderer, &rect);
2922 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2925 static void DrawTouchInputOverlay(void)
2927 static boolean deactivated = TRUE;
2928 static boolean show_grid = FALSE;
2929 static int alpha = 0;
2930 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2931 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2932 boolean active = (overlay.enabled && overlay.active);
2934 if (!active && deactivated)
2939 if (alpha < alpha_max)
2940 alpha = MIN(alpha + alpha_step, alpha_max);
2942 deactivated = FALSE;
2946 alpha = MAX(0, alpha - alpha_step);
2952 if (overlay.show_grid)
2954 else if (deactivated)
2958 DrawTouchInputOverlay_ShowGrid(alpha);
2960 DrawTouchInputOverlay_ShowGridButtons(alpha);
2963 static void DrawTouchGadgetsOverlay(void)
2965 DrawGadgets_OverlayTouchButtons();