1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
18 #define ENABLE_UNUSED_CODE 0 // currently unused functions
20 #define DEBUG_JOYSTICKS 0
23 // ============================================================================
25 // ============================================================================
27 // SDL internal variables
28 static SDL_Window *sdl_window = NULL;
29 static SDL_Renderer *sdl_renderer = NULL;
30 static SDL_Texture *sdl_texture_stream = NULL;
31 static SDL_Texture *sdl_texture_target = NULL;
32 static boolean fullscreen_enabled = FALSE;
33 static boolean limit_screen_updates = FALSE;
36 // functions from SGE library
37 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
39 #if defined(USE_TOUCH_INPUT_OVERLAY)
40 // functions to draw overlay graphics for touch device input
41 static void DrawTouchInputOverlay(void);
44 void SDLLimitScreenUpdates(boolean enable)
46 limit_screen_updates = enable;
49 static void FinalizeScreen(int draw_target)
51 // copy global animations to render target buffer, if defined (below border)
52 if (gfx.draw_global_anim_function != NULL)
53 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
55 // copy global masked border to render target buffer, if defined
56 if (gfx.draw_global_border_function != NULL)
57 gfx.draw_global_border_function(draw_target);
59 // copy global animations to render target buffer, if defined (above border)
60 if (gfx.draw_global_anim_function != NULL)
61 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
63 // copy tile selection cursor to render target buffer, if defined (above all)
64 if (gfx.draw_tile_cursor_function != NULL)
65 gfx.draw_tile_cursor_function(draw_target);
68 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
70 static unsigned int update_screen_delay = 0;
71 unsigned int update_screen_delay_value = 50; // (milliseconds)
72 SDL_Surface *screen = backbuffer->surface;
74 if (limit_screen_updates &&
75 !DelayReached(&update_screen_delay, update_screen_delay_value))
78 LimitScreenUpdates(FALSE);
82 static int LastFrameCounter = 0;
83 boolean changed = (FrameCounter != LastFrameCounter);
85 printf("::: FrameCounter == %d [%s]\n", FrameCounter,
86 (changed ? "-" : "SAME FRAME UPDATED"));
88 LastFrameCounter = FrameCounter;
97 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
98 gfx.final_screen_bitmap != NULL) // may not be initialized yet
100 // draw global animations using bitmaps instead of using textures
101 // to prevent texture scaling artefacts (this is potentially slower)
103 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
104 gfx.win_xsize, gfx.win_ysize, 0, 0);
106 FinalizeScreen(DRAW_TO_SCREEN);
108 screen = gfx.final_screen_bitmap->surface;
110 // force full window redraw
114 SDL_Texture *sdl_texture = sdl_texture_stream;
116 // deactivate use of target texture if render targets are not supported
117 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
118 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
119 sdl_texture_target == NULL)
120 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
122 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
123 sdl_texture = sdl_texture_target;
127 int bytes_x = screen->pitch / video.width;
128 int bytes_y = screen->pitch;
130 SDL_UpdateTexture(sdl_texture, rect,
131 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
136 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
139 int xoff = video.screen_xoffset;
140 int yoff = video.screen_yoffset;
141 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
142 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
143 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
145 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
146 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
147 dst_rect2 = &dst_rect_screen;
149 dst_rect1 = &dst_rect_screen;
151 #if defined(HAS_SCREEN_KEYBOARD)
152 if (video.shifted_up || video.shifted_up_delay)
154 int time_current = SDL_GetTicks();
155 int pos = video.shifted_up_pos;
156 int pos_last = video.shifted_up_pos_last;
158 if (!DelayReachedExt(&video.shifted_up_delay, video.shifted_up_delay_value,
161 int delay = time_current - video.shifted_up_delay;
162 int delay_value = video.shifted_up_delay_value;
164 pos = pos_last + (pos - pos_last) * delay / delay_value;
168 video.shifted_up_pos_last = pos;
169 video.shifted_up_delay = 0;
172 SDL_Rect src_rect_up = { 0, pos, video.width, video.height - pos };
173 SDL_Rect dst_rect_up = { xoff, yoff, video.width, video.height - pos };
175 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
176 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
178 src_rect2 = &src_rect_up;
179 dst_rect2 = &dst_rect_up;
183 src_rect1 = &src_rect_up;
184 dst_rect1 = &dst_rect_up;
189 // clear render target buffer
190 SDL_RenderClear(sdl_renderer);
192 // set renderer to use target texture for rendering
193 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
194 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
195 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
197 // copy backbuffer texture to render target buffer
198 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
199 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
201 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
202 FinalizeScreen(DRAW_TO_SCREEN);
204 // when using target texture, copy it to screen buffer
205 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
206 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
208 SDL_SetRenderTarget(sdl_renderer, NULL);
209 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
212 #if defined(USE_TOUCH_INPUT_OVERLAY)
213 // draw overlay graphics for touch device input, if needed
214 DrawTouchInputOverlay();
217 // global synchronization point of the game to align video frame delay
218 if (with_frame_delay)
219 WaitUntilDelayReached(&video.frame_delay, video.frame_delay_value);
221 video.frame_counter++;
223 // show render target buffer on screen
224 SDL_RenderPresent(sdl_renderer);
227 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
229 PumpEvents(); // execute event filter actions while waiting
231 UpdateScreenExt(rect, TRUE);
234 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
236 UpdateScreenExt(rect, FALSE);
239 void Delay_WithScreenUpdates(unsigned int delay)
241 unsigned int time_current = SDL_GetTicks();
242 unsigned int time_delayed = time_current + delay;
244 while (time_current < time_delayed)
246 // updating the screen contains waiting for frame delay (non-busy)
247 UpdateScreen_WithFrameDelay(NULL);
249 time_current = SDL_GetTicks();
253 static void SDLSetWindowIcon(char *basename)
255 // (setting the window icon on Mac OS X would replace the high-quality
256 // dock icon with the currently smaller (and uglier) icon from file)
258 #if !defined(PLATFORM_MACOSX)
259 char *filename = getCustomImageFilename(basename);
260 SDL_Surface *surface;
262 if (filename == NULL)
264 Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
269 if ((surface = IMG_Load(filename)) == NULL)
271 Error(ERR_WARN, "IMG_Load() failed: %s", SDL_GetError());
276 // set transparent color
277 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
278 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
280 SDL_SetWindowIcon(sdl_window, surface);
284 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
285 SDL_PixelFormat *format2)
287 return (format1->format == format2->format &&
288 format1->BitsPerPixel == format2->BitsPerPixel &&
289 format1->BytesPerPixel == format2->BytesPerPixel &&
290 format1->Rmask == format2->Rmask &&
291 format1->Gmask == format2->Gmask &&
292 format1->Bmask == format2->Bmask);
295 static Pixel SDLGetColorKey(SDL_Surface *surface)
299 if (SDL_GetColorKey(surface, &color_key) != 0)
305 static boolean SDLHasColorKey(SDL_Surface *surface)
307 return (SDLGetColorKey(surface) != -1);
310 static boolean SDLHasAlpha(SDL_Surface *surface)
312 SDL_BlendMode blend_mode;
314 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
317 return (blend_mode == SDL_BLENDMODE_BLEND);
320 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
322 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
324 SDL_SetSurfaceBlendMode(surface, blend_mode);
325 SDL_SetSurfaceAlphaMod(surface, alpha);
328 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
330 SDL_PixelFormat format;
331 SDL_Surface *new_surface;
336 if (backbuffer && backbuffer->surface)
338 format = *backbuffer->surface->format;
339 format.Amask = surface->format->Amask; // keep alpha channel
343 format = *surface->format;
346 new_surface = SDL_ConvertSurface(surface, &format, 0);
348 if (new_surface == NULL)
349 Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
354 boolean SDLSetNativeSurface(SDL_Surface **surface)
356 SDL_Surface *new_surface;
358 if (surface == NULL ||
360 backbuffer == NULL ||
361 backbuffer->surface == NULL)
364 // if pixel format already optimized for destination surface, do nothing
365 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
368 new_surface = SDLGetNativeSurface(*surface);
370 SDL_FreeSurface(*surface);
372 *surface = new_surface;
377 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
379 if (program.headless)
382 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
385 Error(ERR_EXIT, "SDL_CreateTextureFromSurface() failed: %s",
391 void SDLCreateBitmapTextures(Bitmap *bitmap)
397 SDL_DestroyTexture(bitmap->texture);
398 if (bitmap->texture_masked)
399 SDL_DestroyTexture(bitmap->texture_masked);
401 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
402 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
405 void SDLFreeBitmapTextures(Bitmap *bitmap)
411 SDL_DestroyTexture(bitmap->texture);
412 if (bitmap->texture_masked)
413 SDL_DestroyTexture(bitmap->texture_masked);
415 bitmap->texture = NULL;
416 bitmap->texture_masked = NULL;
419 void SDLInitVideoDisplay(void)
421 // initialize SDL video
422 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
423 Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
425 // set default SDL depth
426 video.default_depth = 32; // (how to determine video depth in SDL2?)
428 // Code used with SDL 1.2:
429 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
432 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
434 if (program.headless)
437 video.window_scaling_percent = setup.window_scaling_percent;
438 video.window_scaling_quality = setup.window_scaling_quality;
440 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
442 // SDL 2.0: support for (desktop) fullscreen mode available
443 video.fullscreen_available = TRUE;
445 // open SDL video output device (window or fullscreen mode)
446 if (!SDLSetVideoMode(fullscreen))
447 Error(ERR_EXIT, "setting video mode failed");
449 // !!! SDL2 can only set the window icon if the window already exists !!!
451 SDLSetWindowIcon(program.icon_filename);
453 // set window and icon title
457 static void SDLInitVideoBuffer_DrawBuffer(void)
459 /* SDL cannot directly draw to the visible video framebuffer like X11,
460 but always uses a backbuffer, which is then blitted to the visible
461 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
462 visible video framebuffer with 'SDL_Flip', if the hardware supports
463 this). Therefore do not use an additional backbuffer for drawing, but
464 use a symbolic buffer (distinguishable from the SDL backbuffer) called
465 'window', which indicates that the SDL backbuffer should be updated to
466 the visible video framebuffer when attempting to blit to it.
468 For convenience, it seems to be a good idea to create this symbolic
469 buffer 'window' at the same size as the SDL backbuffer. Although it
470 should never be drawn to directly, it would do no harm nevertheless. */
472 // create additional (symbolic) buffer for double-buffering
473 ReCreateBitmap(&window, video.width, video.height);
475 // create dummy drawing buffer for headless mode, if needed
476 if (program.headless)
477 ReCreateBitmap(&backbuffer, video.width, video.height);
480 void SDLInitVideoBuffer(boolean fullscreen)
482 SDLInitVideoBuffer_VideoBuffer(fullscreen);
483 SDLInitVideoBuffer_DrawBuffer();
486 static boolean SDLCreateScreen(boolean fullscreen)
488 SDL_Surface *new_surface = NULL;
490 int surface_flags_window = SURFACE_FLAGS | SDL_WINDOW_RESIZABLE;
491 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
494 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
496 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
497 _without_ enabling 2D/3D acceleration and/or guest additions installed,
498 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
499 it will try to use accelerated graphics and apparently fails miserably) */
500 int renderer_flags = SDL_RENDERER_SOFTWARE;
503 SDLSetScreenSizeAndOffsets(video.width, video.height);
505 int width = video.width;
506 int height = video.height;
507 int screen_width = video.screen_width;
508 int screen_height = video.screen_height;
509 int surface_flags = (fullscreen ? surface_flags_fullscreen :
510 surface_flags_window);
512 // default window size is unscaled
513 video.window_width = screen_width;
514 video.window_height = screen_height;
516 // store if initial screen mode is fullscreen mode when changing screen size
517 video.fullscreen_initial = fullscreen;
519 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
521 video.window_width = window_scaling_factor * screen_width;
522 video.window_height = window_scaling_factor * screen_height;
524 if (sdl_texture_stream)
526 SDL_DestroyTexture(sdl_texture_stream);
527 sdl_texture_stream = NULL;
530 if (sdl_texture_target)
532 SDL_DestroyTexture(sdl_texture_target);
533 sdl_texture_target = NULL;
536 if (!(fullscreen && fullscreen_enabled))
540 SDL_DestroyRenderer(sdl_renderer);
546 SDL_DestroyWindow(sdl_window);
551 if (sdl_window == NULL)
552 sdl_window = SDL_CreateWindow(program.window_title,
553 SDL_WINDOWPOS_CENTERED,
554 SDL_WINDOWPOS_CENTERED,
559 if (sdl_window != NULL)
561 if (sdl_renderer == NULL)
562 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
564 if (sdl_renderer != NULL)
566 SDL_RenderSetLogicalSize(sdl_renderer, screen_width, screen_height);
567 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
568 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
570 SDLSetScreenVsyncMode(setup.vsync_mode);
572 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
573 SDL_PIXELFORMAT_ARGB8888,
574 SDL_TEXTUREACCESS_STREAMING,
577 if (SDL_RenderTargetSupported(sdl_renderer))
578 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
579 SDL_PIXELFORMAT_ARGB8888,
580 SDL_TEXTUREACCESS_TARGET,
583 if (sdl_texture_stream != NULL)
585 // use SDL default values for RGB masks and no alpha channel
586 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
588 if (new_surface == NULL)
589 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
593 Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
598 Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
603 Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
606 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
607 if (new_surface != NULL)
608 fullscreen_enabled = fullscreen;
610 if (backbuffer == NULL)
611 backbuffer = CreateBitmapStruct();
613 backbuffer->width = video.width;
614 backbuffer->height = video.height;
616 if (backbuffer->surface)
617 SDL_FreeSurface(backbuffer->surface);
619 backbuffer->surface = new_surface;
621 return (new_surface != NULL);
624 boolean SDLSetVideoMode(boolean fullscreen)
626 boolean success = FALSE;
630 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
632 // switch display to fullscreen mode, if available
633 success = SDLCreateScreen(TRUE);
637 // switching display to fullscreen mode failed -- do not try it again
638 video.fullscreen_available = FALSE;
642 video.fullscreen_enabled = TRUE;
646 if ((!fullscreen && video.fullscreen_enabled) || !success)
648 // switch display to window mode
649 success = SDLCreateScreen(FALSE);
653 // switching display to window mode failed -- should not happen
657 video.fullscreen_enabled = FALSE;
658 video.window_scaling_percent = setup.window_scaling_percent;
659 video.window_scaling_quality = setup.window_scaling_quality;
661 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
665 SDLRedrawWindow(); // map window
670 void SDLSetWindowTitle(void)
672 if (sdl_window == NULL)
675 SDL_SetWindowTitle(sdl_window, program.window_title);
678 void SDLSetWindowScaling(int window_scaling_percent)
680 if (sdl_window == NULL)
683 float window_scaling_factor = (float)window_scaling_percent / 100;
684 int new_window_width = (int)(window_scaling_factor * video.screen_width);
685 int new_window_height = (int)(window_scaling_factor * video.screen_height);
687 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
689 video.window_scaling_percent = window_scaling_percent;
690 video.window_width = new_window_width;
691 video.window_height = new_window_height;
696 void SDLSetWindowScalingQuality(char *window_scaling_quality)
698 SDL_Texture *new_texture;
700 if (sdl_texture_stream == NULL)
703 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
705 new_texture = SDL_CreateTexture(sdl_renderer,
706 SDL_PIXELFORMAT_ARGB8888,
707 SDL_TEXTUREACCESS_STREAMING,
708 video.width, video.height);
710 if (new_texture != NULL)
712 SDL_DestroyTexture(sdl_texture_stream);
714 sdl_texture_stream = new_texture;
717 if (SDL_RenderTargetSupported(sdl_renderer))
718 new_texture = SDL_CreateTexture(sdl_renderer,
719 SDL_PIXELFORMAT_ARGB8888,
720 SDL_TEXTUREACCESS_TARGET,
721 video.width, video.height);
725 if (new_texture != NULL)
727 SDL_DestroyTexture(sdl_texture_target);
729 sdl_texture_target = new_texture;
734 video.window_scaling_quality = window_scaling_quality;
737 void SDLSetWindowFullscreen(boolean fullscreen)
739 if (sdl_window == NULL)
742 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
744 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
745 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
747 // if screen size was changed in fullscreen mode, correct desktop window size
748 if (!fullscreen && video.fullscreen_initial)
750 SDLSetWindowScaling(setup.window_scaling_percent);
751 SDL_SetWindowPosition(sdl_window,
752 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
754 video.fullscreen_initial = FALSE;
758 void SDLSetDisplaySize(void)
760 SDL_Rect display_bounds;
762 SDL_GetDisplayBounds(0, &display_bounds);
764 video.display_width = display_bounds.w;
765 video.display_height = display_bounds.h;
768 Error(ERR_DEBUG, "SDL real screen size: %d x %d",
769 video.display_width, video.display_height);
773 void SDLSetScreenSizeAndOffsets(int width, int height)
775 // set default video screen size and offsets
776 video.screen_width = width;
777 video.screen_height = height;
778 video.screen_xoffset = 0;
779 video.screen_yoffset = 0;
781 #if defined(USE_COMPLETE_DISPLAY)
782 float ratio_video = (float) width / height;
783 float ratio_display = (float) video.display_width / video.display_height;
785 if (ratio_video != ratio_display)
787 // adjust drawable screen size to cover the whole device display
789 if (ratio_video < ratio_display)
790 video.screen_width *= ratio_display / ratio_video;
792 video.screen_height *= ratio_video / ratio_display;
794 video.screen_xoffset = (video.screen_width - width) / 2;
795 video.screen_yoffset = (video.screen_height - height) / 2;
798 Error(ERR_DEBUG, "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
800 video.screen_width, video.screen_height,
801 ratio_video, ratio_display);
807 void SDLSetScreenSizeForRenderer(int width, int height)
809 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
812 void SDLSetScreenProperties(void)
814 SDLSetScreenSizeAndOffsets(video.width, video.height);
815 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
818 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
820 video.screen_rendering_mode =
821 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
822 SPECIAL_RENDERING_BITMAP :
823 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
824 SPECIAL_RENDERING_TARGET:
825 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
826 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
829 void SDLSetScreenVsyncMode(char *vsync_mode)
832 (strEqual(vsync_mode, STR_VSYNC_MODE_NORMAL) ? VSYNC_MODE_NORMAL :
833 strEqual(vsync_mode, STR_VSYNC_MODE_ADAPTIVE) ? VSYNC_MODE_ADAPTIVE :
835 int result = SDL_GL_SetSwapInterval(interval);
837 // if adaptive vsync requested, but not supported, retry with normal vsync
838 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
839 SDL_GL_SetSwapInterval(VSYNC_MODE_NORMAL);
842 void SDLRedrawWindow(void)
844 UpdateScreen_WithoutFrameDelay(NULL);
847 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
850 if (program.headless)
853 SDL_Surface *surface =
854 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
857 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
859 SDLSetNativeSurface(&surface);
861 bitmap->surface = surface;
864 void SDLFreeBitmapPointers(Bitmap *bitmap)
867 SDL_FreeSurface(bitmap->surface);
868 if (bitmap->surface_masked)
869 SDL_FreeSurface(bitmap->surface_masked);
871 bitmap->surface = NULL;
872 bitmap->surface_masked = NULL;
875 SDL_DestroyTexture(bitmap->texture);
876 if (bitmap->texture_masked)
877 SDL_DestroyTexture(bitmap->texture_masked);
879 bitmap->texture = NULL;
880 bitmap->texture_masked = NULL;
883 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
884 int src_x, int src_y, int width, int height,
885 int dst_x, int dst_y, int mask_mode)
887 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
888 SDL_Rect src_rect, dst_rect;
900 // if (src_bitmap != backbuffer || dst_bitmap != window)
901 if (!(src_bitmap == backbuffer && dst_bitmap == window))
902 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
903 src_bitmap->surface_masked : src_bitmap->surface),
904 &src_rect, real_dst_bitmap->surface, &dst_rect);
906 if (dst_bitmap == window)
907 UpdateScreen_WithFrameDelay(&dst_rect);
910 void SDLBlitTexture(Bitmap *bitmap,
911 int src_x, int src_y, int width, int height,
912 int dst_x, int dst_y, int mask_mode)
914 SDL_Texture *texture;
919 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
934 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
937 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
940 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
948 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
950 if (dst_bitmap == window)
951 UpdateScreen_WithFrameDelay(&rect);
954 void PrepareFadeBitmap(int draw_target)
956 Bitmap *fade_bitmap =
957 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
958 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
960 if (fade_bitmap == NULL)
963 // copy backbuffer to fading buffer
964 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
966 // add border and animations to fading buffer
967 FinalizeScreen(draw_target);
970 void SDLFadeRectangle(int x, int y, int width, int height,
971 int fade_mode, int fade_delay, int post_delay,
972 void (*draw_border_function)(void))
974 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
975 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
976 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
977 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
978 SDL_Surface *surface_screen = backbuffer->surface;
979 SDL_Rect src_rect, dst_rect;
981 int src_x = x, src_y = y;
982 int dst_x = x, dst_y = y;
983 unsigned int time_last, time_current;
985 // store function for drawing global masked border
986 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
988 // deactivate drawing of global border while fading, if needed
989 if (draw_border_function == NULL)
990 gfx.draw_global_border_function = NULL;
999 dst_rect.w = width; // (ignored)
1000 dst_rect.h = height; // (ignored)
1002 dst_rect2 = dst_rect;
1004 // before fading in, store backbuffer (without animation graphics)
1005 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1006 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1008 // copy source and target surfaces to temporary surfaces for fading
1009 if (fade_mode & FADE_TYPE_TRANSFORM)
1011 // (source and target fading buffer already prepared)
1013 else if (fade_mode & FADE_TYPE_FADE_IN)
1015 // (target fading buffer already prepared)
1016 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1018 else // FADE_TYPE_FADE_OUT
1020 // (source fading buffer already prepared)
1021 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1024 time_current = SDL_GetTicks();
1026 if (fade_mode == FADE_MODE_MELT)
1028 boolean done = FALSE;
1029 int melt_pixels = 2;
1030 int melt_columns = width / melt_pixels;
1031 int ypos[melt_columns];
1032 int max_steps = height / 8 + 32;
1037 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1039 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1041 ypos[0] = -GetSimpleRandom(16);
1043 for (i = 1 ; i < melt_columns; i++)
1045 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1047 ypos[i] = ypos[i - 1] + r;
1060 time_last = time_current;
1061 time_current = SDL_GetTicks();
1062 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1063 steps_final = MIN(MAX(0, steps), max_steps);
1067 done = (steps_done >= steps_final);
1069 for (i = 0 ; i < melt_columns; i++)
1077 else if (ypos[i] < height)
1082 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1084 if (ypos[i] + dy >= height)
1085 dy = height - ypos[i];
1087 // copy part of (appearing) target surface to upper area
1088 src_rect.x = src_x + i * melt_pixels;
1089 // src_rect.y = src_y + ypos[i];
1091 src_rect.w = melt_pixels;
1093 src_rect.h = ypos[i] + dy;
1095 dst_rect.x = dst_x + i * melt_pixels;
1096 // dst_rect.y = dst_y + ypos[i];
1099 if (steps_done >= steps_final)
1100 SDL_BlitSurface(surface_target, &src_rect,
1101 surface_screen, &dst_rect);
1105 // copy part of (disappearing) source surface to lower area
1106 src_rect.x = src_x + i * melt_pixels;
1108 src_rect.w = melt_pixels;
1109 src_rect.h = height - ypos[i];
1111 dst_rect.x = dst_x + i * melt_pixels;
1112 dst_rect.y = dst_y + ypos[i];
1114 if (steps_done >= steps_final)
1115 SDL_BlitSurface(surface_source, &src_rect,
1116 surface_screen, &dst_rect);
1122 src_rect.x = src_x + i * melt_pixels;
1124 src_rect.w = melt_pixels;
1125 src_rect.h = height;
1127 dst_rect.x = dst_x + i * melt_pixels;
1130 if (steps_done >= steps_final)
1131 SDL_BlitSurface(surface_target, &src_rect,
1132 surface_screen, &dst_rect);
1136 if (steps_done >= steps_final)
1138 if (draw_border_function != NULL)
1139 draw_border_function();
1141 UpdateScreen_WithFrameDelay(&dst_rect2);
1145 else if (fade_mode == FADE_MODE_CURTAIN)
1149 int xx_size = width / 2;
1151 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1153 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1155 for (xx = 0; xx < xx_size;)
1157 time_last = time_current;
1158 time_current = SDL_GetTicks();
1159 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1160 xx_final = MIN(MAX(0, xx), xx_size);
1165 src_rect.h = height;
1170 // draw new (target) image to screen buffer
1171 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1173 if (xx_final < xx_size)
1175 src_rect.w = xx_size - xx_final;
1176 src_rect.h = height;
1178 // draw old (source) image to screen buffer (left side)
1180 src_rect.x = src_x + xx_final;
1183 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1185 // draw old (source) image to screen buffer (right side)
1187 src_rect.x = src_x + xx_size;
1188 dst_rect.x = dst_x + xx_size + xx_final;
1190 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1193 if (draw_border_function != NULL)
1194 draw_border_function();
1196 // only update the region of the screen that is affected from fading
1197 UpdateScreen_WithFrameDelay(&dst_rect2);
1200 else // fading in, fading out or cross-fading
1205 for (alpha = 0.0; alpha < 255.0;)
1207 time_last = time_current;
1208 time_current = SDL_GetTicks();
1209 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1210 alpha_final = MIN(MAX(0, alpha), 255);
1212 // draw existing (source) image to screen buffer
1213 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1215 // draw new (target) image to screen buffer using alpha blending
1216 SDLSetAlpha(surface_target, TRUE, alpha_final);
1217 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1219 if (draw_border_function != NULL)
1220 draw_border_function();
1222 // only update the region of the screen that is affected from fading
1223 UpdateScreen_WithFrameDelay(&dst_rect);
1228 Delay_WithScreenUpdates(post_delay);
1230 // restore function for drawing global masked border
1231 gfx.draw_global_border_function = draw_global_border_function;
1233 // after fading in, restore backbuffer (without animation graphics)
1234 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1235 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1238 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1239 int to_x, int to_y, Uint32 color)
1241 SDL_Surface *surface = dst_bitmap->surface;
1245 swap_numbers(&from_x, &to_x);
1248 swap_numbers(&from_y, &to_y);
1252 rect.w = (to_x - from_x + 1);
1253 rect.h = (to_y - from_y + 1);
1255 SDL_FillRect(surface, &rect, color);
1258 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1259 int to_x, int to_y, Uint32 color)
1261 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1264 #if ENABLE_UNUSED_CODE
1265 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1266 int num_points, Uint32 color)
1271 for (i = 0; i < num_points - 1; i++)
1273 for (x = 0; x < line_width; x++)
1275 for (y = 0; y < line_width; y++)
1277 int dx = x - line_width / 2;
1278 int dy = y - line_width / 2;
1280 if ((x == 0 && y == 0) ||
1281 (x == 0 && y == line_width - 1) ||
1282 (x == line_width - 1 && y == 0) ||
1283 (x == line_width - 1 && y == line_width - 1))
1286 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1287 points[i+1].x + dx, points[i+1].y + dy, color);
1294 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1296 SDL_Surface *surface = src_bitmap->surface;
1298 switch (surface->format->BytesPerPixel)
1300 case 1: // assuming 8-bpp
1302 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1306 case 2: // probably 15-bpp or 16-bpp
1308 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1312 case 3: // slow 24-bpp mode; usually not used
1315 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1319 shift = surface->format->Rshift;
1320 color |= *(pix + shift / 8) >> shift;
1321 shift = surface->format->Gshift;
1322 color |= *(pix + shift / 8) >> shift;
1323 shift = surface->format->Bshift;
1324 color |= *(pix + shift / 8) >> shift;
1330 case 4: // probably 32-bpp
1332 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1341 // ============================================================================
1342 // The following functions were taken from the SGE library
1343 // (SDL Graphics Extension Library) by Anders Lindström
1344 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1345 // ============================================================================
1347 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1349 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1351 switch (surface->format->BytesPerPixel)
1356 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1362 // Probably 15-bpp or 16-bpp
1363 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1369 // Slow 24-bpp mode, usually not used
1373 // Gack - slow, but endian correct
1374 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1375 shift = surface->format->Rshift;
1376 *(pix+shift/8) = color>>shift;
1377 shift = surface->format->Gshift;
1378 *(pix+shift/8) = color>>shift;
1379 shift = surface->format->Bshift;
1380 *(pix+shift/8) = color>>shift;
1387 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1395 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1396 Uint8 R, Uint8 G, Uint8 B)
1398 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1401 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1403 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1406 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1408 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1411 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1416 // Gack - slow, but endian correct
1417 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1418 shift = surface->format->Rshift;
1419 *(pix+shift/8) = color>>shift;
1420 shift = surface->format->Gshift;
1421 *(pix+shift/8) = color>>shift;
1422 shift = surface->format->Bshift;
1423 *(pix+shift/8) = color>>shift;
1426 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1428 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1431 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1433 switch (dest->format->BytesPerPixel)
1436 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1440 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1444 _PutPixel24(dest,x,y,color);
1448 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1454 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1456 if (SDL_MUSTLOCK(surface))
1458 if (SDL_LockSurface(surface) < 0)
1464 _PutPixel(surface, x, y, color);
1466 if (SDL_MUSTLOCK(surface))
1468 SDL_UnlockSurface(surface);
1473 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1474 Uint8 r, Uint8 g, Uint8 b)
1476 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1479 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1481 if (y >= 0 && y <= dest->h - 1)
1483 switch (dest->format->BytesPerPixel)
1486 return y*dest->pitch;
1490 return y*dest->pitch/2;
1494 return y*dest->pitch;
1498 return y*dest->pitch/4;
1506 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1509 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1511 switch (surface->format->BytesPerPixel)
1516 *((Uint8 *)surface->pixels + ypitch + x) = color;
1522 // Probably 15-bpp or 16-bpp
1523 *((Uint16 *)surface->pixels + ypitch + x) = color;
1529 // Slow 24-bpp mode, usually not used
1533 // Gack - slow, but endian correct
1534 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1535 shift = surface->format->Rshift;
1536 *(pix+shift/8) = color>>shift;
1537 shift = surface->format->Gshift;
1538 *(pix+shift/8) = color>>shift;
1539 shift = surface->format->Bshift;
1540 *(pix+shift/8) = color>>shift;
1547 *((Uint32 *)surface->pixels + ypitch + x) = color;
1554 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1559 if (SDL_MUSTLOCK(Surface))
1561 if (SDL_LockSurface(Surface) < 0)
1575 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1579 if (x2 > Surface->w - 1)
1580 x2 = Surface->w - 1;
1587 SDL_FillRect(Surface, &l, Color);
1589 if (SDL_MUSTLOCK(Surface))
1591 SDL_UnlockSurface(Surface);
1595 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1596 Uint8 R, Uint8 G, Uint8 B)
1598 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1601 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1614 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1618 if (x2 > Surface->w - 1)
1619 x2 = Surface->w - 1;
1626 SDL_FillRect(Surface, &l, Color);
1629 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1634 if (SDL_MUSTLOCK(Surface))
1636 if (SDL_LockSurface(Surface) < 0)
1650 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1654 if (y2 > Surface->h - 1)
1655 y2 = Surface->h - 1;
1662 SDL_FillRect(Surface, &l, Color);
1664 if (SDL_MUSTLOCK(Surface))
1666 SDL_UnlockSurface(Surface);
1670 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1671 Uint8 R, Uint8 G, Uint8 B)
1673 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1676 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1689 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1693 if (y2 > Surface->h - 1)
1694 y2 = Surface->h - 1;
1701 SDL_FillRect(Surface, &l, Color);
1705 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1706 Sint16 x2, Sint16 y2, Uint32 Color,
1707 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1710 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1715 sdx = (dx < 0) ? -1 : 1;
1716 sdy = (dy < 0) ? -1 : 1;
1728 for (x = 0; x < dx; x++)
1730 Callback(Surface, px, py, Color);
1744 for (y = 0; y < dy; y++)
1746 Callback(Surface, px, py, Color);
1761 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1762 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1763 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1766 sge_DoLine(Surface, X1, Y1, X2, Y2,
1767 SDL_MapRGB(Surface->format, R, G, B), Callback);
1771 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1774 if (SDL_MUSTLOCK(Surface))
1776 if (SDL_LockSurface(Surface) < 0)
1781 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1783 // unlock the display
1784 if (SDL_MUSTLOCK(Surface))
1786 SDL_UnlockSurface(Surface);
1791 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1792 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1794 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1798 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1800 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1804 // ----------------------------------------------------------------------------
1805 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1806 // ----------------------------------------------------------------------------
1808 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1809 int width, int height, Uint32 color)
1813 for (y = src_y; y < src_y + height; y++)
1815 for (x = src_x; x < src_x + width; x++)
1817 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1819 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1824 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1825 int src_x, int src_y, int width, int height,
1826 int dst_x, int dst_y)
1830 for (y = 0; y < height; y++)
1832 for (x = 0; x < width; x++)
1834 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1836 if (pixel != BLACK_PIXEL)
1837 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1843 // ============================================================================
1844 // The following functions were taken from the SDL_gfx library version 2.0.3
1845 // (Rotozoomer) by Andreas Schiffler
1846 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1847 // ============================================================================
1849 // ----------------------------------------------------------------------------
1852 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1853 // ----------------------------------------------------------------------------
1863 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1866 tColorRGBA *sp, *csp, *dp;
1870 sp = csp = (tColorRGBA *) src->pixels;
1871 dp = (tColorRGBA *) dst->pixels;
1872 dgap = dst->pitch - dst->w * 4;
1874 for (y = 0; y < dst->h; y++)
1878 for (x = 0; x < dst->w; x++)
1880 tColorRGBA *sp0 = sp;
1881 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1882 tColorRGBA *sp00 = &sp0[0];
1883 tColorRGBA *sp01 = &sp0[1];
1884 tColorRGBA *sp10 = &sp1[0];
1885 tColorRGBA *sp11 = &sp1[1];
1888 // create new color pixel from all four source color pixels
1889 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1890 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1891 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1892 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1897 // advance source pointers
1900 // advance destination pointer
1904 // advance source pointer
1905 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1907 // advance destination pointers
1908 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1914 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1916 int x, y, *sax, *say, *csax, *csay;
1918 tColorRGBA *sp, *csp, *csp0, *dp;
1921 // use specialized zoom function when scaling down to exactly half size
1922 if (src->w == 2 * dst->w &&
1923 src->h == 2 * dst->h)
1924 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1927 sx = (float) src->w / (float) dst->w;
1928 sy = (float) src->h / (float) dst->h;
1930 // allocate memory for row increments
1931 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1932 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1934 // precalculate row increments
1935 for (x = 0; x <= dst->w; x++)
1936 *csax++ = (int)(sx * x);
1938 for (y = 0; y <= dst->h; y++)
1939 *csay++ = (int)(sy * y);
1942 sp = csp = csp0 = (tColorRGBA *) src->pixels;
1943 dp = (tColorRGBA *) dst->pixels;
1944 dgap = dst->pitch - dst->w * 4;
1947 for (y = 0; y < dst->h; y++)
1952 for (x = 0; x < dst->w; x++)
1957 // advance source pointers
1961 // advance destination pointer
1965 // advance source pointer
1967 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
1969 // advance destination pointers
1970 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1979 // ----------------------------------------------------------------------------
1982 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
1983 // ----------------------------------------------------------------------------
1985 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
1987 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1988 Uint8 *sp, *dp, *csp;
1992 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
1993 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
1995 // allocate memory for row increments
1996 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
1997 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
1999 // precalculate row increments
2002 for (x = 0; x < dst->w; x++)
2005 *csax = (csx >> 16);
2012 for (y = 0; y < dst->h; y++)
2015 *csay = (csy >> 16);
2022 for (x = 0; x < dst->w; x++)
2030 for (y = 0; y < dst->h; y++)
2037 sp = csp = (Uint8 *) src->pixels;
2038 dp = (Uint8 *) dst->pixels;
2039 dgap = dst->pitch - dst->w;
2043 for (y = 0; y < dst->h; y++)
2047 for (x = 0; x < dst->w; x++)
2052 // advance source pointers
2056 // advance destination pointer
2060 // advance source pointer (for row)
2061 csp += ((*csay) * src->pitch);
2064 // advance destination pointers
2074 // ----------------------------------------------------------------------------
2077 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2078 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2079 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2080 // into a 32bit RGBA format on the fly.
2081 // ----------------------------------------------------------------------------
2083 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2085 SDL_Surface *zoom_src = NULL;
2086 SDL_Surface *zoom_dst = NULL;
2087 boolean is_converted = FALSE;
2094 // determine if source surface is 32 bit or 8 bit
2095 is_32bit = (src->format->BitsPerPixel == 32);
2097 if (is_32bit || src->format->BitsPerPixel == 8)
2099 // use source surface 'as is'
2104 // new source surface is 32 bit with a defined RGB ordering
2105 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2106 0x000000ff, 0x0000ff00, 0x00ff0000,
2107 (src->format->Amask ? 0xff000000 : 0));
2108 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2110 is_converted = TRUE;
2113 // allocate surface to completely contain the zoomed surface
2116 // target surface is 32 bit with source RGBA/ABGR ordering
2117 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2118 zoom_src->format->Rmask,
2119 zoom_src->format->Gmask,
2120 zoom_src->format->Bmask,
2121 zoom_src->format->Amask);
2125 // target surface is 8 bit
2126 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2130 // lock source surface
2131 SDL_LockSurface(zoom_src);
2133 // check which kind of surface we have
2136 // call the 32 bit transformation routine to do the zooming
2137 zoomSurfaceRGBA(zoom_src, zoom_dst);
2142 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2143 zoom_dst->format->palette->colors[i] =
2144 zoom_src->format->palette->colors[i];
2145 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2147 // call the 8 bit transformation routine to do the zooming
2148 zoomSurfaceY(zoom_src, zoom_dst);
2151 // unlock source surface
2152 SDL_UnlockSurface(zoom_src);
2154 // free temporary surface
2156 SDL_FreeSurface(zoom_src);
2158 // return destination surface
2162 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2164 SDL_Surface *new_surface;
2166 if (surface == NULL)
2169 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2170 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2172 // remove alpha channel from native non-transparent surface, if defined
2173 SDLSetAlpha(new_surface, FALSE, 0);
2175 // remove transparent color from native non-transparent surface, if defined
2176 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2181 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2183 Bitmap *dst_bitmap = CreateBitmapStruct();
2184 SDL_Surface *src_surface = src_bitmap->surface_masked;
2185 SDL_Surface *dst_surface;
2187 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2188 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2190 dst_bitmap->width = dst_width;
2191 dst_bitmap->height = dst_height;
2193 // create zoomed temporary surface from source surface
2194 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2196 // create native format destination surface from zoomed temporary surface
2197 SDLSetNativeSurface(&dst_surface);
2199 // set color key for zoomed surface from source surface, if defined
2200 if (SDLHasColorKey(src_surface))
2201 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2202 SDLGetColorKey(src_surface));
2204 // create native non-transparent surface for opaque blitting
2205 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2207 // set native transparent surface for masked blitting
2208 dst_bitmap->surface_masked = dst_surface;
2214 // ============================================================================
2215 // load image to bitmap
2216 // ============================================================================
2218 Bitmap *SDLLoadImage(char *filename)
2220 Bitmap *new_bitmap = CreateBitmapStruct();
2221 SDL_Surface *sdl_image_tmp;
2223 if (program.headless)
2225 // prevent sanity check warnings at later stage
2226 new_bitmap->width = new_bitmap->height = 1;
2231 print_timestamp_init("SDLLoadImage");
2233 print_timestamp_time(getBaseNamePtr(filename));
2235 // load image to temporary surface
2236 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2237 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
2239 print_timestamp_time("IMG_Load");
2241 UPDATE_BUSY_STATE();
2243 // create native non-transparent surface for current image
2244 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2245 Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2247 print_timestamp_time("SDLGetNativeSurface (opaque)");
2249 UPDATE_BUSY_STATE();
2251 // set black pixel to transparent if no alpha channel / transparent color
2252 if (!SDLHasAlpha(sdl_image_tmp) &&
2253 !SDLHasColorKey(sdl_image_tmp))
2254 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2255 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2257 // create native transparent surface for current image
2258 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2259 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2261 print_timestamp_time("SDLGetNativeSurface (masked)");
2263 UPDATE_BUSY_STATE();
2265 // free temporary surface
2266 SDL_FreeSurface(sdl_image_tmp);
2268 new_bitmap->width = new_bitmap->surface->w;
2269 new_bitmap->height = new_bitmap->surface->h;
2271 print_timestamp_done("SDLLoadImage");
2277 // ----------------------------------------------------------------------------
2278 // custom cursor fuctions
2279 // ----------------------------------------------------------------------------
2281 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2283 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2284 cursor_info->width, cursor_info->height,
2285 cursor_info->hot_x, cursor_info->hot_y);
2288 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2290 static struct MouseCursorInfo *last_cursor_info = NULL;
2291 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2292 static SDL_Cursor *cursor_default = NULL;
2293 static SDL_Cursor *cursor_current = NULL;
2295 // if invoked for the first time, store the SDL default cursor
2296 if (cursor_default == NULL)
2297 cursor_default = SDL_GetCursor();
2299 // only create new cursor if cursor info (custom only) has changed
2300 if (cursor_info != NULL && cursor_info != last_cursor_info)
2302 cursor_current = create_cursor(cursor_info);
2303 last_cursor_info = cursor_info;
2306 // only set new cursor if cursor info (custom or NULL) has changed
2307 if (cursor_info != last_cursor_info2)
2308 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2310 last_cursor_info2 = cursor_info;
2314 // ============================================================================
2316 // ============================================================================
2318 void SDLOpenAudio(void)
2320 if (program.headless)
2323 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2325 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2329 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2330 AUDIO_NUM_CHANNELS_STEREO,
2331 setup.system.audio_fragment_size) < 0)
2333 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2337 audio.sound_available = TRUE;
2338 audio.music_available = TRUE;
2339 audio.loops_available = TRUE;
2340 audio.sound_enabled = TRUE;
2342 // set number of available mixer channels
2343 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2344 audio.music_channel = MUSIC_CHANNEL;
2345 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2347 Mixer_InitChannels();
2350 void SDLCloseAudio(void)
2353 Mix_HaltChannel(-1);
2356 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2360 // ============================================================================
2362 // ============================================================================
2364 void SDLWaitEvent(Event *event)
2366 SDL_WaitEvent(event);
2369 void SDLCorrectRawMousePosition(int *x, int *y)
2371 if (sdl_renderer == NULL)
2374 // this corrects the raw mouse position for logical screen size within event
2375 // filters (correction done later by SDL library when handling mouse events)
2378 float scale_x, scale_y;
2380 SDL_RenderGetViewport(sdl_renderer, &viewport);
2381 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2383 *x = (int)(*x / scale_x);
2384 *y = (int)(*y / scale_y);
2391 // ============================================================================
2392 // joystick functions
2393 // ============================================================================
2395 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2396 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2397 static int sdl_js_axis[MAX_PLAYERS][2];
2398 static int sdl_js_button[MAX_PLAYERS][2];
2399 static boolean sdl_is_controller[MAX_PLAYERS];
2401 void SDLClearJoystickState(void)
2405 for (i = 0; i < MAX_PLAYERS; i++)
2407 for (j = 0; j < 2; j++)
2409 sdl_js_axis_raw[i][j] = -1;
2410 sdl_js_axis[i][j] = 0;
2411 sdl_js_button[i][j] = 0;
2416 boolean SDLOpenJoystick(int nr)
2418 if (nr < 0 || nr >= MAX_PLAYERS)
2421 sdl_is_controller[nr] = SDL_IsGameController(nr);
2424 Error(ERR_DEBUG, "opening joystick %d (%s)",
2425 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2428 if (sdl_is_controller[nr])
2429 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2431 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2433 return (sdl_joystick[nr] != NULL);
2436 void SDLCloseJoystick(int nr)
2438 if (nr < 0 || nr >= MAX_PLAYERS)
2442 Error(ERR_DEBUG, "closing joystick %d", nr);
2445 if (sdl_is_controller[nr])
2446 SDL_GameControllerClose(sdl_joystick[nr]);
2448 SDL_JoystickClose(sdl_joystick[nr]);
2450 sdl_joystick[nr] = NULL;
2453 boolean SDLCheckJoystickOpened(int nr)
2455 if (nr < 0 || nr >= MAX_PLAYERS)
2458 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2461 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2463 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2464 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2465 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2466 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2468 if (nr < 0 || nr >= MAX_PLAYERS)
2474 // prevent (slightly jittering, but centered) axis A from resetting axis B
2475 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2476 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2479 sdl_js_axis[nr][axis_id] = axis_value;
2480 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2483 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2485 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2486 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2487 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2488 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2489 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2490 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2491 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2492 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2495 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2496 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2497 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2498 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2499 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2500 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2501 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2502 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2504 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2505 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2506 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2507 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2508 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2510 if (nr < 0 || nr >= MAX_PLAYERS)
2513 if (button_id == -1)
2516 sdl_js_button[nr][button_id] = button_state;
2519 void HandleJoystickEvent(Event *event)
2523 case SDL_CONTROLLERDEVICEADDED:
2525 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2526 event->cdevice.which);
2531 case SDL_CONTROLLERDEVICEREMOVED:
2533 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2534 event->cdevice.which);
2539 case SDL_CONTROLLERAXISMOTION:
2541 Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2542 event->caxis.which, event->caxis.axis, event->caxis.value);
2544 setJoystickAxis(event->caxis.which,
2546 event->caxis.value);
2549 case SDL_CONTROLLERBUTTONDOWN:
2551 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2552 event->cbutton.which, event->cbutton.button);
2554 setJoystickButton(event->cbutton.which,
2555 event->cbutton.button,
2559 case SDL_CONTROLLERBUTTONUP:
2561 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2562 event->cbutton.which, event->cbutton.button);
2564 setJoystickButton(event->cbutton.which,
2565 event->cbutton.button,
2569 case SDL_JOYAXISMOTION:
2570 if (sdl_is_controller[event->jaxis.which])
2574 Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2575 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2577 if (event->jaxis.axis < 4)
2578 setJoystickAxis(event->jaxis.which,
2580 event->jaxis.value);
2583 case SDL_JOYBUTTONDOWN:
2584 if (sdl_is_controller[event->jaxis.which])
2588 Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2589 event->jbutton.which, event->jbutton.button);
2591 if (event->jbutton.button < 4)
2592 setJoystickButton(event->jbutton.which,
2593 event->jbutton.button,
2597 case SDL_JOYBUTTONUP:
2598 if (sdl_is_controller[event->jaxis.which])
2602 Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2603 event->jbutton.which, event->jbutton.button);
2605 if (event->jbutton.button < 4)
2606 setJoystickButton(event->jbutton.which,
2607 event->jbutton.button,
2616 void SDLInitJoysticks(void)
2618 static boolean sdl_joystick_subsystem_initialized = FALSE;
2619 boolean print_warning = !sdl_joystick_subsystem_initialized;
2620 char *mappings_file_base = getPath2(options.conf_directory,
2621 GAMECONTROLLER_BASENAME);
2622 char *mappings_file_user = getPath2(getUserGameDataDir(),
2623 GAMECONTROLLER_BASENAME);
2627 if (!sdl_joystick_subsystem_initialized)
2629 sdl_joystick_subsystem_initialized = TRUE;
2631 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2633 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2635 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2639 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2641 // the included game controller base mappings should always be found
2642 if (num_mappings == -1)
2643 Error(ERR_WARN, "no game controller base mappings found");
2646 Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2649 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2652 // the personal game controller user mappings may or may not be found
2653 if (num_mappings == -1)
2654 Error(ERR_WARN, "no game controller user mappings found");
2656 Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2658 Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2661 checked_free(mappings_file_base);
2662 checked_free(mappings_file_user);
2665 for (i = 0; i < SDL_NumJoysticks(); i++)
2667 const char *name, *type;
2669 if (SDL_IsGameController(i))
2671 name = SDL_GameControllerNameForIndex(i);
2672 type = "game controller";
2676 name = SDL_JoystickNameForIndex(i);
2680 Error(ERR_INFO, "- joystick %d (%s): '%s'",
2681 i, type, (name ? name : "(Unknown)"));
2686 // assign joysticks from configured to connected joystick for all players
2687 for (i = 0; i < MAX_PLAYERS; i++)
2689 // get configured joystick for this player
2690 char *device_name = setup.input[i].joy.device_name;
2691 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2693 if (joystick_nr >= SDL_NumJoysticks())
2695 if (setup.input[i].use_joystick && print_warning)
2696 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2701 // store configured joystick number for each player
2702 joystick.nr[i] = joystick_nr;
2705 // now open all connected joysticks (regardless if configured or not)
2706 for (i = 0; i < SDL_NumJoysticks(); i++)
2708 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2709 if (SDLCheckJoystickOpened(i))
2710 SDLCloseJoystick(i);
2712 if (SDLOpenJoystick(i))
2713 joystick.status = JOYSTICK_ACTIVATED;
2714 else if (print_warning)
2715 Error(ERR_WARN, "cannot open joystick %d", i);
2718 SDLClearJoystickState();
2721 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2723 if (nr < 0 || nr >= MAX_PLAYERS)
2727 *x = sdl_js_axis[nr][0];
2729 *y = sdl_js_axis[nr][1];
2732 *b1 = sdl_js_button[nr][0];
2734 *b2 = sdl_js_button[nr][1];
2740 // ============================================================================
2741 // touch input overlay functions
2742 // ============================================================================
2744 #if defined(USE_TOUCH_INPUT_OVERLAY)
2745 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2748 int grid_xsize = overlay.grid_xsize;
2749 int grid_ysize = overlay.grid_ysize;
2752 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2753 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2755 for (x = 0; x < grid_xsize; x++)
2757 rect.x = (x + 0) * video.screen_width / grid_xsize;
2758 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2760 for (y = 0; y < grid_ysize; y++)
2762 rect.y = (y + 0) * video.screen_height / grid_ysize;
2763 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2765 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2766 SDL_RenderDrawRect(sdl_renderer, &rect);
2770 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2773 static void RenderFillRectangle(int x, int y, int width, int height)
2775 SDL_Rect rect = { x, y, width, height };
2777 SDL_RenderFillRect(sdl_renderer, &rect);
2780 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2782 static int alpha_direction = 0;
2783 static int alpha_highlight = 0;
2784 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2785 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2787 int grid_xsize = overlay.grid_xsize;
2788 int grid_ysize = overlay.grid_ysize;
2791 if (alpha == alpha_max)
2793 if (alpha_direction < 0)
2795 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2797 if (alpha_highlight == 0)
2798 alpha_direction = 1;
2802 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2804 if (alpha_highlight == alpha_max)
2805 alpha_direction = -1;
2810 alpha_direction = 1;
2811 alpha_highlight = alpha;
2814 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2816 for (x = 0; x < grid_xsize; x++)
2818 for (y = 0; y < grid_ysize; y++)
2820 int grid_button = overlay.grid_button[x][y];
2821 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2822 int alpha_draw = alpha;
2823 int outline_border = MV_NONE;
2824 int border_size = 2;
2825 boolean draw_outlined = setup.touch.draw_outlined;
2826 boolean draw_pressed = setup.touch.draw_pressed;
2828 if (grid_button == CHAR_GRID_BUTTON_NONE)
2831 if (grid_button == overlay.grid_button_highlight)
2832 alpha_draw = alpha_highlight;
2834 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2837 draw_outlined = FALSE;
2839 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2842 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2844 rect.x = (x + 0) * video.screen_width / grid_xsize;
2845 rect.y = (y + 0) * video.screen_height / grid_ysize;
2846 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2847 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2849 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2851 rect.x += border_size;
2852 rect.w -= border_size;
2854 outline_border |= MV_LEFT;
2857 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2859 rect.w -= border_size;
2861 outline_border |= MV_RIGHT;
2864 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2866 rect.y += border_size;
2867 rect.h -= border_size;
2869 outline_border |= MV_UP;
2872 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2874 rect.h -= border_size;
2876 outline_border |= MV_DOWN;
2881 int rect_x = rect.x +
2882 (outline_border & MV_LEFT ? border_size : 0);
2883 int rect_w = rect.w -
2884 (outline_border & MV_LEFT ? border_size : 0) -
2885 (outline_border & MV_RIGHT ? border_size : 0);
2887 if (outline_border & MV_LEFT)
2888 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2890 if (outline_border & MV_RIGHT)
2891 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2892 border_size, rect.h);
2894 if (outline_border & MV_UP)
2895 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2897 if (outline_border & MV_DOWN)
2898 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
2899 rect_w, border_size);
2903 SDL_RenderFillRect(sdl_renderer, &rect);
2908 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2911 static void DrawTouchInputOverlay(void)
2913 static boolean deactivated = TRUE;
2914 static boolean show_grid = FALSE;
2915 static int alpha = 0;
2916 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2917 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2918 boolean active = (overlay.enabled && overlay.active);
2920 if (!active && deactivated)
2925 if (alpha < alpha_max)
2926 alpha = MIN(alpha + alpha_step, alpha_max);
2928 deactivated = FALSE;
2932 alpha = MAX(0, alpha - alpha_step);
2938 if (overlay.show_grid)
2940 else if (deactivated)
2944 DrawTouchInputOverlay_ShowGrid(alpha);
2946 DrawTouchInputOverlay_ShowGridButtons(alpha);