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 // show render target buffer on screen
222 SDL_RenderPresent(sdl_renderer);
225 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
227 PumpEvents(); // execute event filter actions while waiting
229 UpdateScreenExt(rect, TRUE);
232 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
234 UpdateScreenExt(rect, FALSE);
237 void Delay_WithScreenUpdates(unsigned int delay)
239 unsigned int time_current = SDL_GetTicks();
240 unsigned int time_delayed = time_current + delay;
242 while (time_current < time_delayed)
244 // updating the screen contains waiting for frame delay (non-busy)
245 UpdateScreen_WithFrameDelay(NULL);
247 time_current = SDL_GetTicks();
251 static void SDLSetWindowIcon(char *basename)
253 // (setting the window icon on Mac OS X would replace the high-quality
254 // dock icon with the currently smaller (and uglier) icon from file)
256 #if !defined(PLATFORM_MACOSX)
257 char *filename = getCustomImageFilename(basename);
258 SDL_Surface *surface;
260 if (filename == NULL)
262 Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
267 if ((surface = IMG_Load(filename)) == NULL)
269 Error(ERR_WARN, "IMG_Load() failed: %s", SDL_GetError());
274 // set transparent color
275 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
276 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
278 SDL_SetWindowIcon(sdl_window, surface);
282 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
283 SDL_PixelFormat *format2)
285 return (format1->format == format2->format &&
286 format1->BitsPerPixel == format2->BitsPerPixel &&
287 format1->BytesPerPixel == format2->BytesPerPixel &&
288 format1->Rmask == format2->Rmask &&
289 format1->Gmask == format2->Gmask &&
290 format1->Bmask == format2->Bmask);
293 static Pixel SDLGetColorKey(SDL_Surface *surface)
297 if (SDL_GetColorKey(surface, &color_key) != 0)
303 static boolean SDLHasColorKey(SDL_Surface *surface)
305 return (SDLGetColorKey(surface) != -1);
308 static boolean SDLHasAlpha(SDL_Surface *surface)
310 SDL_BlendMode blend_mode;
312 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
315 return (blend_mode == SDL_BLENDMODE_BLEND);
318 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
320 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
322 SDL_SetSurfaceBlendMode(surface, blend_mode);
323 SDL_SetSurfaceAlphaMod(surface, alpha);
326 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
328 SDL_PixelFormat format;
329 SDL_Surface *new_surface;
334 if (backbuffer && backbuffer->surface)
336 format = *backbuffer->surface->format;
337 format.Amask = surface->format->Amask; // keep alpha channel
341 format = *surface->format;
344 new_surface = SDL_ConvertSurface(surface, &format, 0);
346 if (new_surface == NULL)
347 Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
352 boolean SDLSetNativeSurface(SDL_Surface **surface)
354 SDL_Surface *new_surface;
356 if (surface == NULL ||
358 backbuffer == NULL ||
359 backbuffer->surface == NULL)
362 // if pixel format already optimized for destination surface, do nothing
363 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
366 new_surface = SDLGetNativeSurface(*surface);
368 SDL_FreeSurface(*surface);
370 *surface = new_surface;
375 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
377 if (program.headless)
380 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
383 Error(ERR_EXIT, "SDL_CreateTextureFromSurface() failed: %s",
389 void SDLCreateBitmapTextures(Bitmap *bitmap)
395 SDL_DestroyTexture(bitmap->texture);
396 if (bitmap->texture_masked)
397 SDL_DestroyTexture(bitmap->texture_masked);
399 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
400 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
403 void SDLFreeBitmapTextures(Bitmap *bitmap)
409 SDL_DestroyTexture(bitmap->texture);
410 if (bitmap->texture_masked)
411 SDL_DestroyTexture(bitmap->texture_masked);
413 bitmap->texture = NULL;
414 bitmap->texture_masked = NULL;
417 void SDLInitVideoDisplay(void)
419 // initialize SDL video
420 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
421 Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
423 // set default SDL depth
424 video.default_depth = 32; // (how to determine video depth in SDL2?)
426 // Code used with SDL 1.2:
427 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
430 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
432 if (program.headless)
435 video.window_scaling_percent = setup.window_scaling_percent;
436 video.window_scaling_quality = setup.window_scaling_quality;
438 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
440 // SDL 2.0: support for (desktop) fullscreen mode available
441 video.fullscreen_available = TRUE;
443 // open SDL video output device (window or fullscreen mode)
444 if (!SDLSetVideoMode(fullscreen))
445 Error(ERR_EXIT, "setting video mode failed");
447 // !!! SDL2 can only set the window icon if the window already exists !!!
449 SDLSetWindowIcon(program.icon_filename);
451 // set window and icon title
455 static void SDLInitVideoBuffer_DrawBuffer(void)
457 /* SDL cannot directly draw to the visible video framebuffer like X11,
458 but always uses a backbuffer, which is then blitted to the visible
459 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
460 visible video framebuffer with 'SDL_Flip', if the hardware supports
461 this). Therefore do not use an additional backbuffer for drawing, but
462 use a symbolic buffer (distinguishable from the SDL backbuffer) called
463 'window', which indicates that the SDL backbuffer should be updated to
464 the visible video framebuffer when attempting to blit to it.
466 For convenience, it seems to be a good idea to create this symbolic
467 buffer 'window' at the same size as the SDL backbuffer. Although it
468 should never be drawn to directly, it would do no harm nevertheless. */
470 // create additional (symbolic) buffer for double-buffering
471 ReCreateBitmap(&window, video.width, video.height);
473 // create dummy drawing buffer for headless mode, if needed
474 if (program.headless)
475 ReCreateBitmap(&backbuffer, video.width, video.height);
478 void SDLInitVideoBuffer(boolean fullscreen)
480 SDLInitVideoBuffer_VideoBuffer(fullscreen);
481 SDLInitVideoBuffer_DrawBuffer();
484 static boolean SDLCreateScreen(boolean fullscreen)
486 SDL_Surface *new_surface = NULL;
488 int surface_flags_window = SURFACE_FLAGS | SDL_WINDOW_RESIZABLE;
489 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
492 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
494 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
495 _without_ enabling 2D/3D acceleration and/or guest additions installed,
496 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
497 it will try to use accelerated graphics and apparently fails miserably) */
498 int renderer_flags = SDL_RENDERER_SOFTWARE;
501 SDLSetScreenSizeAndOffsets(video.width, video.height);
503 int width = video.width;
504 int height = video.height;
505 int screen_width = video.screen_width;
506 int screen_height = video.screen_height;
507 int surface_flags = (fullscreen ? surface_flags_fullscreen :
508 surface_flags_window);
510 // default window size is unscaled
511 video.window_width = screen_width;
512 video.window_height = screen_height;
514 // store if initial screen mode is fullscreen mode when changing screen size
515 video.fullscreen_initial = fullscreen;
517 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
519 video.window_width = window_scaling_factor * screen_width;
520 video.window_height = window_scaling_factor * screen_height;
522 if (sdl_texture_stream)
524 SDL_DestroyTexture(sdl_texture_stream);
525 sdl_texture_stream = NULL;
528 if (sdl_texture_target)
530 SDL_DestroyTexture(sdl_texture_target);
531 sdl_texture_target = NULL;
534 if (!(fullscreen && fullscreen_enabled))
538 SDL_DestroyRenderer(sdl_renderer);
544 SDL_DestroyWindow(sdl_window);
549 if (sdl_window == NULL)
550 sdl_window = SDL_CreateWindow(program.window_title,
551 SDL_WINDOWPOS_CENTERED,
552 SDL_WINDOWPOS_CENTERED,
557 if (sdl_window != NULL)
559 if (sdl_renderer == NULL)
560 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
562 if (sdl_renderer != NULL)
564 SDL_RenderSetLogicalSize(sdl_renderer, screen_width, screen_height);
565 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
566 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
568 SDLSetScreenVsyncMode(setup.vsync_mode);
570 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
571 SDL_PIXELFORMAT_ARGB8888,
572 SDL_TEXTUREACCESS_STREAMING,
575 if (SDL_RenderTargetSupported(sdl_renderer))
576 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
577 SDL_PIXELFORMAT_ARGB8888,
578 SDL_TEXTUREACCESS_TARGET,
581 if (sdl_texture_stream != NULL)
583 // use SDL default values for RGB masks and no alpha channel
584 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
586 if (new_surface == NULL)
587 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
591 Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
596 Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
601 Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
604 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
605 if (new_surface != NULL)
606 fullscreen_enabled = fullscreen;
608 if (backbuffer == NULL)
609 backbuffer = CreateBitmapStruct();
611 backbuffer->width = video.width;
612 backbuffer->height = video.height;
614 if (backbuffer->surface)
615 SDL_FreeSurface(backbuffer->surface);
617 backbuffer->surface = new_surface;
619 return (new_surface != NULL);
622 boolean SDLSetVideoMode(boolean fullscreen)
624 boolean success = FALSE;
628 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
630 // switch display to fullscreen mode, if available
631 success = SDLCreateScreen(TRUE);
635 // switching display to fullscreen mode failed -- do not try it again
636 video.fullscreen_available = FALSE;
640 video.fullscreen_enabled = TRUE;
644 if ((!fullscreen && video.fullscreen_enabled) || !success)
646 // switch display to window mode
647 success = SDLCreateScreen(FALSE);
651 // switching display to window mode failed -- should not happen
655 video.fullscreen_enabled = FALSE;
656 video.window_scaling_percent = setup.window_scaling_percent;
657 video.window_scaling_quality = setup.window_scaling_quality;
659 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
663 SDLRedrawWindow(); // map window
668 void SDLSetWindowTitle(void)
670 if (sdl_window == NULL)
673 SDL_SetWindowTitle(sdl_window, program.window_title);
676 void SDLSetWindowScaling(int window_scaling_percent)
678 if (sdl_window == NULL)
681 float window_scaling_factor = (float)window_scaling_percent / 100;
682 int new_window_width = (int)(window_scaling_factor * video.screen_width);
683 int new_window_height = (int)(window_scaling_factor * video.screen_height);
685 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
687 video.window_scaling_percent = window_scaling_percent;
688 video.window_width = new_window_width;
689 video.window_height = new_window_height;
694 void SDLSetWindowScalingQuality(char *window_scaling_quality)
696 SDL_Texture *new_texture;
698 if (sdl_texture_stream == NULL)
701 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
703 new_texture = SDL_CreateTexture(sdl_renderer,
704 SDL_PIXELFORMAT_ARGB8888,
705 SDL_TEXTUREACCESS_STREAMING,
706 video.width, video.height);
708 if (new_texture != NULL)
710 SDL_DestroyTexture(sdl_texture_stream);
712 sdl_texture_stream = new_texture;
715 if (SDL_RenderTargetSupported(sdl_renderer))
716 new_texture = SDL_CreateTexture(sdl_renderer,
717 SDL_PIXELFORMAT_ARGB8888,
718 SDL_TEXTUREACCESS_TARGET,
719 video.width, video.height);
723 if (new_texture != NULL)
725 SDL_DestroyTexture(sdl_texture_target);
727 sdl_texture_target = new_texture;
732 video.window_scaling_quality = window_scaling_quality;
735 void SDLSetWindowFullscreen(boolean fullscreen)
737 if (sdl_window == NULL)
740 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
742 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
743 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
745 // if screen size was changed in fullscreen mode, correct desktop window size
746 if (!fullscreen && video.fullscreen_initial)
748 SDLSetWindowScaling(setup.window_scaling_percent);
749 SDL_SetWindowPosition(sdl_window,
750 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
752 video.fullscreen_initial = FALSE;
756 void SDLSetDisplaySize(void)
758 SDL_Rect display_bounds;
760 SDL_GetDisplayBounds(0, &display_bounds);
762 video.display_width = display_bounds.w;
763 video.display_height = display_bounds.h;
766 Error(ERR_DEBUG, "SDL real screen size: %d x %d",
767 video.display_width, video.display_height);
771 void SDLSetScreenSizeAndOffsets(int width, int height)
773 // set default video screen size and offsets
774 video.screen_width = width;
775 video.screen_height = height;
776 video.screen_xoffset = 0;
777 video.screen_yoffset = 0;
779 #if defined(USE_COMPLETE_DISPLAY)
780 float ratio_video = (float) width / height;
781 float ratio_display = (float) video.display_width / video.display_height;
783 if (ratio_video != ratio_display)
785 // adjust drawable screen size to cover the whole device display
787 if (ratio_video < ratio_display)
788 video.screen_width *= ratio_display / ratio_video;
790 video.screen_height *= ratio_video / ratio_display;
792 video.screen_xoffset = (video.screen_width - width) / 2;
793 video.screen_yoffset = (video.screen_height - height) / 2;
796 Error(ERR_DEBUG, "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
798 video.screen_width, video.screen_height,
799 ratio_video, ratio_display);
805 void SDLSetScreenSizeForRenderer(int width, int height)
807 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
810 void SDLSetScreenProperties(void)
812 SDLSetScreenSizeAndOffsets(video.width, video.height);
813 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
816 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
818 video.screen_rendering_mode =
819 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
820 SPECIAL_RENDERING_BITMAP :
821 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
822 SPECIAL_RENDERING_TARGET:
823 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
824 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
827 void SDLSetScreenVsyncMode(char *vsync_mode)
830 (strEqual(vsync_mode, STR_VSYNC_MODE_NORMAL) ? VSYNC_MODE_NORMAL :
831 strEqual(vsync_mode, STR_VSYNC_MODE_ADAPTIVE) ? VSYNC_MODE_ADAPTIVE :
833 int result = SDL_GL_SetSwapInterval(interval);
835 // if adaptive vsync requested, but not supported, retry with normal vsync
836 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
837 SDL_GL_SetSwapInterval(VSYNC_MODE_NORMAL);
840 void SDLRedrawWindow(void)
842 UpdateScreen_WithoutFrameDelay(NULL);
845 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
848 if (program.headless)
851 SDL_Surface *surface =
852 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
855 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
857 SDLSetNativeSurface(&surface);
859 bitmap->surface = surface;
862 void SDLFreeBitmapPointers(Bitmap *bitmap)
865 SDL_FreeSurface(bitmap->surface);
866 if (bitmap->surface_masked)
867 SDL_FreeSurface(bitmap->surface_masked);
869 bitmap->surface = NULL;
870 bitmap->surface_masked = NULL;
873 SDL_DestroyTexture(bitmap->texture);
874 if (bitmap->texture_masked)
875 SDL_DestroyTexture(bitmap->texture_masked);
877 bitmap->texture = NULL;
878 bitmap->texture_masked = NULL;
881 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
882 int src_x, int src_y, int width, int height,
883 int dst_x, int dst_y, int mask_mode)
885 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
886 SDL_Rect src_rect, dst_rect;
898 // if (src_bitmap != backbuffer || dst_bitmap != window)
899 if (!(src_bitmap == backbuffer && dst_bitmap == window))
900 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
901 src_bitmap->surface_masked : src_bitmap->surface),
902 &src_rect, real_dst_bitmap->surface, &dst_rect);
904 if (dst_bitmap == window)
905 UpdateScreen_WithFrameDelay(&dst_rect);
908 void SDLBlitTexture(Bitmap *bitmap,
909 int src_x, int src_y, int width, int height,
910 int dst_x, int dst_y, int mask_mode)
912 SDL_Texture *texture;
917 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
932 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
935 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
938 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
946 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
948 if (dst_bitmap == window)
949 UpdateScreen_WithFrameDelay(&rect);
952 void PrepareFadeBitmap(int draw_target)
954 Bitmap *fade_bitmap =
955 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
956 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
958 if (fade_bitmap == NULL)
961 // copy backbuffer to fading buffer
962 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
964 // add border and animations to fading buffer
965 FinalizeScreen(draw_target);
968 void SDLFadeRectangle(int x, int y, int width, int height,
969 int fade_mode, int fade_delay, int post_delay,
970 void (*draw_border_function)(void))
972 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
973 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
974 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
975 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
976 SDL_Surface *surface_screen = backbuffer->surface;
977 SDL_Rect src_rect, dst_rect;
979 int src_x = x, src_y = y;
980 int dst_x = x, dst_y = y;
981 unsigned int time_last, time_current;
983 // store function for drawing global masked border
984 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
986 // deactivate drawing of global border while fading, if needed
987 if (draw_border_function == NULL)
988 gfx.draw_global_border_function = NULL;
997 dst_rect.w = width; // (ignored)
998 dst_rect.h = height; // (ignored)
1000 dst_rect2 = dst_rect;
1002 // before fading in, store backbuffer (without animation graphics)
1003 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1004 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1006 // copy source and target surfaces to temporary surfaces for fading
1007 if (fade_mode & FADE_TYPE_TRANSFORM)
1009 // (source and target fading buffer already prepared)
1011 else if (fade_mode & FADE_TYPE_FADE_IN)
1013 // (target fading buffer already prepared)
1014 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1016 else // FADE_TYPE_FADE_OUT
1018 // (source fading buffer already prepared)
1019 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1022 time_current = SDL_GetTicks();
1024 if (fade_mode == FADE_MODE_MELT)
1026 boolean done = FALSE;
1027 int melt_pixels = 2;
1028 int melt_columns = width / melt_pixels;
1029 int ypos[melt_columns];
1030 int max_steps = height / 8 + 32;
1035 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1037 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1039 ypos[0] = -GetSimpleRandom(16);
1041 for (i = 1 ; i < melt_columns; i++)
1043 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1045 ypos[i] = ypos[i - 1] + r;
1058 time_last = time_current;
1059 time_current = SDL_GetTicks();
1060 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1061 steps_final = MIN(MAX(0, steps), max_steps);
1065 done = (steps_done >= steps_final);
1067 for (i = 0 ; i < melt_columns; i++)
1075 else if (ypos[i] < height)
1080 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1082 if (ypos[i] + dy >= height)
1083 dy = height - ypos[i];
1085 // copy part of (appearing) target surface to upper area
1086 src_rect.x = src_x + i * melt_pixels;
1087 // src_rect.y = src_y + ypos[i];
1089 src_rect.w = melt_pixels;
1091 src_rect.h = ypos[i] + dy;
1093 dst_rect.x = dst_x + i * melt_pixels;
1094 // dst_rect.y = dst_y + ypos[i];
1097 if (steps_done >= steps_final)
1098 SDL_BlitSurface(surface_target, &src_rect,
1099 surface_screen, &dst_rect);
1103 // copy part of (disappearing) source surface to lower area
1104 src_rect.x = src_x + i * melt_pixels;
1106 src_rect.w = melt_pixels;
1107 src_rect.h = height - ypos[i];
1109 dst_rect.x = dst_x + i * melt_pixels;
1110 dst_rect.y = dst_y + ypos[i];
1112 if (steps_done >= steps_final)
1113 SDL_BlitSurface(surface_source, &src_rect,
1114 surface_screen, &dst_rect);
1120 src_rect.x = src_x + i * melt_pixels;
1122 src_rect.w = melt_pixels;
1123 src_rect.h = height;
1125 dst_rect.x = dst_x + i * melt_pixels;
1128 if (steps_done >= steps_final)
1129 SDL_BlitSurface(surface_target, &src_rect,
1130 surface_screen, &dst_rect);
1134 if (steps_done >= steps_final)
1136 if (draw_border_function != NULL)
1137 draw_border_function();
1139 UpdateScreen_WithFrameDelay(&dst_rect2);
1143 else if (fade_mode == FADE_MODE_CURTAIN)
1147 int xx_size = width / 2;
1149 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1151 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1153 for (xx = 0; xx < xx_size;)
1155 time_last = time_current;
1156 time_current = SDL_GetTicks();
1157 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1158 xx_final = MIN(MAX(0, xx), xx_size);
1163 src_rect.h = height;
1168 // draw new (target) image to screen buffer
1169 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1171 if (xx_final < xx_size)
1173 src_rect.w = xx_size - xx_final;
1174 src_rect.h = height;
1176 // draw old (source) image to screen buffer (left side)
1178 src_rect.x = src_x + xx_final;
1181 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1183 // draw old (source) image to screen buffer (right side)
1185 src_rect.x = src_x + xx_size;
1186 dst_rect.x = dst_x + xx_size + xx_final;
1188 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1191 if (draw_border_function != NULL)
1192 draw_border_function();
1194 // only update the region of the screen that is affected from fading
1195 UpdateScreen_WithFrameDelay(&dst_rect2);
1198 else // fading in, fading out or cross-fading
1203 for (alpha = 0.0; alpha < 255.0;)
1205 time_last = time_current;
1206 time_current = SDL_GetTicks();
1207 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1208 alpha_final = MIN(MAX(0, alpha), 255);
1210 // draw existing (source) image to screen buffer
1211 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1213 // draw new (target) image to screen buffer using alpha blending
1214 SDLSetAlpha(surface_target, TRUE, alpha_final);
1215 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1217 if (draw_border_function != NULL)
1218 draw_border_function();
1220 // only update the region of the screen that is affected from fading
1221 UpdateScreen_WithFrameDelay(&dst_rect);
1226 Delay_WithScreenUpdates(post_delay);
1228 // restore function for drawing global masked border
1229 gfx.draw_global_border_function = draw_global_border_function;
1231 // after fading in, restore backbuffer (without animation graphics)
1232 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1233 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1236 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1237 int to_x, int to_y, Uint32 color)
1239 SDL_Surface *surface = dst_bitmap->surface;
1243 swap_numbers(&from_x, &to_x);
1246 swap_numbers(&from_y, &to_y);
1250 rect.w = (to_x - from_x + 1);
1251 rect.h = (to_y - from_y + 1);
1253 SDL_FillRect(surface, &rect, color);
1256 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1257 int to_x, int to_y, Uint32 color)
1259 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1262 #if ENABLE_UNUSED_CODE
1263 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1264 int num_points, Uint32 color)
1269 for (i = 0; i < num_points - 1; i++)
1271 for (x = 0; x < line_width; x++)
1273 for (y = 0; y < line_width; y++)
1275 int dx = x - line_width / 2;
1276 int dy = y - line_width / 2;
1278 if ((x == 0 && y == 0) ||
1279 (x == 0 && y == line_width - 1) ||
1280 (x == line_width - 1 && y == 0) ||
1281 (x == line_width - 1 && y == line_width - 1))
1284 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1285 points[i+1].x + dx, points[i+1].y + dy, color);
1292 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1294 SDL_Surface *surface = src_bitmap->surface;
1296 switch (surface->format->BytesPerPixel)
1298 case 1: // assuming 8-bpp
1300 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1304 case 2: // probably 15-bpp or 16-bpp
1306 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1310 case 3: // slow 24-bpp mode; usually not used
1313 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1317 shift = surface->format->Rshift;
1318 color |= *(pix + shift / 8) >> shift;
1319 shift = surface->format->Gshift;
1320 color |= *(pix + shift / 8) >> shift;
1321 shift = surface->format->Bshift;
1322 color |= *(pix + shift / 8) >> shift;
1328 case 4: // probably 32-bpp
1330 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1339 // ============================================================================
1340 // The following functions were taken from the SGE library
1341 // (SDL Graphics Extension Library) by Anders Lindström
1342 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1343 // ============================================================================
1345 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1347 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1349 switch (surface->format->BytesPerPixel)
1354 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1360 // Probably 15-bpp or 16-bpp
1361 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1367 // Slow 24-bpp mode, usually not used
1371 // Gack - slow, but endian correct
1372 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1373 shift = surface->format->Rshift;
1374 *(pix+shift/8) = color>>shift;
1375 shift = surface->format->Gshift;
1376 *(pix+shift/8) = color>>shift;
1377 shift = surface->format->Bshift;
1378 *(pix+shift/8) = color>>shift;
1385 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1393 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1394 Uint8 R, Uint8 G, Uint8 B)
1396 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1399 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1401 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1404 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1406 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1409 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1414 // Gack - slow, but endian correct
1415 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1416 shift = surface->format->Rshift;
1417 *(pix+shift/8) = color>>shift;
1418 shift = surface->format->Gshift;
1419 *(pix+shift/8) = color>>shift;
1420 shift = surface->format->Bshift;
1421 *(pix+shift/8) = color>>shift;
1424 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1426 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1429 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1431 switch (dest->format->BytesPerPixel)
1434 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1438 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1442 _PutPixel24(dest,x,y,color);
1446 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1452 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1454 if (SDL_MUSTLOCK(surface))
1456 if (SDL_LockSurface(surface) < 0)
1462 _PutPixel(surface, x, y, color);
1464 if (SDL_MUSTLOCK(surface))
1466 SDL_UnlockSurface(surface);
1471 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1472 Uint8 r, Uint8 g, Uint8 b)
1474 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1477 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1479 if (y >= 0 && y <= dest->h - 1)
1481 switch (dest->format->BytesPerPixel)
1484 return y*dest->pitch;
1488 return y*dest->pitch/2;
1492 return y*dest->pitch;
1496 return y*dest->pitch/4;
1504 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1507 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1509 switch (surface->format->BytesPerPixel)
1514 *((Uint8 *)surface->pixels + ypitch + x) = color;
1520 // Probably 15-bpp or 16-bpp
1521 *((Uint16 *)surface->pixels + ypitch + x) = color;
1527 // Slow 24-bpp mode, usually not used
1531 // Gack - slow, but endian correct
1532 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1533 shift = surface->format->Rshift;
1534 *(pix+shift/8) = color>>shift;
1535 shift = surface->format->Gshift;
1536 *(pix+shift/8) = color>>shift;
1537 shift = surface->format->Bshift;
1538 *(pix+shift/8) = color>>shift;
1545 *((Uint32 *)surface->pixels + ypitch + x) = color;
1552 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1557 if (SDL_MUSTLOCK(Surface))
1559 if (SDL_LockSurface(Surface) < 0)
1573 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1577 if (x2 > Surface->w - 1)
1578 x2 = Surface->w - 1;
1585 SDL_FillRect(Surface, &l, Color);
1587 if (SDL_MUSTLOCK(Surface))
1589 SDL_UnlockSurface(Surface);
1593 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1594 Uint8 R, Uint8 G, Uint8 B)
1596 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1599 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1612 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1616 if (x2 > Surface->w - 1)
1617 x2 = Surface->w - 1;
1624 SDL_FillRect(Surface, &l, Color);
1627 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1632 if (SDL_MUSTLOCK(Surface))
1634 if (SDL_LockSurface(Surface) < 0)
1648 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1652 if (y2 > Surface->h - 1)
1653 y2 = Surface->h - 1;
1660 SDL_FillRect(Surface, &l, Color);
1662 if (SDL_MUSTLOCK(Surface))
1664 SDL_UnlockSurface(Surface);
1668 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1669 Uint8 R, Uint8 G, Uint8 B)
1671 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1674 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1687 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1691 if (y2 > Surface->h - 1)
1692 y2 = Surface->h - 1;
1699 SDL_FillRect(Surface, &l, Color);
1703 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1704 Sint16 x2, Sint16 y2, Uint32 Color,
1705 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1708 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1713 sdx = (dx < 0) ? -1 : 1;
1714 sdy = (dy < 0) ? -1 : 1;
1726 for (x = 0; x < dx; x++)
1728 Callback(Surface, px, py, Color);
1742 for (y = 0; y < dy; y++)
1744 Callback(Surface, px, py, Color);
1759 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1760 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1761 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1764 sge_DoLine(Surface, X1, Y1, X2, Y2,
1765 SDL_MapRGB(Surface->format, R, G, B), Callback);
1769 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1772 if (SDL_MUSTLOCK(Surface))
1774 if (SDL_LockSurface(Surface) < 0)
1779 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1781 // unlock the display
1782 if (SDL_MUSTLOCK(Surface))
1784 SDL_UnlockSurface(Surface);
1789 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1790 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1792 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1796 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1798 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1802 // ----------------------------------------------------------------------------
1803 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1804 // ----------------------------------------------------------------------------
1806 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1807 int width, int height, Uint32 color)
1811 for (y = src_y; y < src_y + height; y++)
1813 for (x = src_x; x < src_x + width; x++)
1815 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1817 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1822 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1823 int src_x, int src_y, int width, int height,
1824 int dst_x, int dst_y)
1828 for (y = 0; y < height; y++)
1830 for (x = 0; x < width; x++)
1832 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1834 if (pixel != BLACK_PIXEL)
1835 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1841 // ============================================================================
1842 // The following functions were taken from the SDL_gfx library version 2.0.3
1843 // (Rotozoomer) by Andreas Schiffler
1844 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1845 // ============================================================================
1847 // ----------------------------------------------------------------------------
1850 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1851 // ----------------------------------------------------------------------------
1861 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1864 tColorRGBA *sp, *csp, *dp;
1868 sp = csp = (tColorRGBA *) src->pixels;
1869 dp = (tColorRGBA *) dst->pixels;
1870 dgap = dst->pitch - dst->w * 4;
1872 for (y = 0; y < dst->h; y++)
1876 for (x = 0; x < dst->w; x++)
1878 tColorRGBA *sp0 = sp;
1879 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1880 tColorRGBA *sp00 = &sp0[0];
1881 tColorRGBA *sp01 = &sp0[1];
1882 tColorRGBA *sp10 = &sp1[0];
1883 tColorRGBA *sp11 = &sp1[1];
1886 // create new color pixel from all four source color pixels
1887 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1888 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1889 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1890 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1895 // advance source pointers
1898 // advance destination pointer
1902 // advance source pointer
1903 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1905 // advance destination pointers
1906 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1912 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1914 int x, y, *sax, *say, *csax, *csay;
1916 tColorRGBA *sp, *csp, *csp0, *dp;
1919 // use specialized zoom function when scaling down to exactly half size
1920 if (src->w == 2 * dst->w &&
1921 src->h == 2 * dst->h)
1922 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1925 sx = (float) src->w / (float) dst->w;
1926 sy = (float) src->h / (float) dst->h;
1928 // allocate memory for row increments
1929 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1930 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1932 // precalculate row increments
1933 for (x = 0; x <= dst->w; x++)
1934 *csax++ = (int)(sx * x);
1936 for (y = 0; y <= dst->h; y++)
1937 *csay++ = (int)(sy * y);
1940 sp = csp = csp0 = (tColorRGBA *) src->pixels;
1941 dp = (tColorRGBA *) dst->pixels;
1942 dgap = dst->pitch - dst->w * 4;
1945 for (y = 0; y < dst->h; y++)
1950 for (x = 0; x < dst->w; x++)
1955 // advance source pointers
1959 // advance destination pointer
1963 // advance source pointer
1965 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
1967 // advance destination pointers
1968 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1977 // ----------------------------------------------------------------------------
1980 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
1981 // ----------------------------------------------------------------------------
1983 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
1985 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1986 Uint8 *sp, *dp, *csp;
1990 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
1991 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
1993 // allocate memory for row increments
1994 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
1995 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
1997 // precalculate row increments
2000 for (x = 0; x < dst->w; x++)
2003 *csax = (csx >> 16);
2010 for (y = 0; y < dst->h; y++)
2013 *csay = (csy >> 16);
2020 for (x = 0; x < dst->w; x++)
2028 for (y = 0; y < dst->h; y++)
2035 sp = csp = (Uint8 *) src->pixels;
2036 dp = (Uint8 *) dst->pixels;
2037 dgap = dst->pitch - dst->w;
2041 for (y = 0; y < dst->h; y++)
2045 for (x = 0; x < dst->w; x++)
2050 // advance source pointers
2054 // advance destination pointer
2058 // advance source pointer (for row)
2059 csp += ((*csay) * src->pitch);
2062 // advance destination pointers
2072 // ----------------------------------------------------------------------------
2075 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2076 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2077 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2078 // into a 32bit RGBA format on the fly.
2079 // ----------------------------------------------------------------------------
2081 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2083 SDL_Surface *zoom_src = NULL;
2084 SDL_Surface *zoom_dst = NULL;
2085 boolean is_converted = FALSE;
2092 // determine if source surface is 32 bit or 8 bit
2093 is_32bit = (src->format->BitsPerPixel == 32);
2095 if (is_32bit || src->format->BitsPerPixel == 8)
2097 // use source surface 'as is'
2102 // new source surface is 32 bit with a defined RGB ordering
2103 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2104 0x000000ff, 0x0000ff00, 0x00ff0000,
2105 (src->format->Amask ? 0xff000000 : 0));
2106 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2108 is_converted = TRUE;
2111 // allocate surface to completely contain the zoomed surface
2114 // target surface is 32 bit with source RGBA/ABGR ordering
2115 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2116 zoom_src->format->Rmask,
2117 zoom_src->format->Gmask,
2118 zoom_src->format->Bmask,
2119 zoom_src->format->Amask);
2123 // target surface is 8 bit
2124 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2128 // lock source surface
2129 SDL_LockSurface(zoom_src);
2131 // check which kind of surface we have
2134 // call the 32 bit transformation routine to do the zooming
2135 zoomSurfaceRGBA(zoom_src, zoom_dst);
2140 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2141 zoom_dst->format->palette->colors[i] =
2142 zoom_src->format->palette->colors[i];
2143 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2145 // call the 8 bit transformation routine to do the zooming
2146 zoomSurfaceY(zoom_src, zoom_dst);
2149 // unlock source surface
2150 SDL_UnlockSurface(zoom_src);
2152 // free temporary surface
2154 SDL_FreeSurface(zoom_src);
2156 // return destination surface
2160 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2162 SDL_Surface *new_surface;
2164 if (surface == NULL)
2167 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2168 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2170 // remove alpha channel from native non-transparent surface, if defined
2171 SDLSetAlpha(new_surface, FALSE, 0);
2173 // remove transparent color from native non-transparent surface, if defined
2174 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2179 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2181 Bitmap *dst_bitmap = CreateBitmapStruct();
2182 SDL_Surface *src_surface = src_bitmap->surface_masked;
2183 SDL_Surface *dst_surface;
2185 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2186 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2188 dst_bitmap->width = dst_width;
2189 dst_bitmap->height = dst_height;
2191 // create zoomed temporary surface from source surface
2192 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2194 // create native format destination surface from zoomed temporary surface
2195 SDLSetNativeSurface(&dst_surface);
2197 // set color key for zoomed surface from source surface, if defined
2198 if (SDLHasColorKey(src_surface))
2199 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2200 SDLGetColorKey(src_surface));
2202 // create native non-transparent surface for opaque blitting
2203 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2205 // set native transparent surface for masked blitting
2206 dst_bitmap->surface_masked = dst_surface;
2212 // ============================================================================
2213 // load image to bitmap
2214 // ============================================================================
2216 Bitmap *SDLLoadImage(char *filename)
2218 Bitmap *new_bitmap = CreateBitmapStruct();
2219 SDL_Surface *sdl_image_tmp;
2221 if (program.headless)
2223 // prevent sanity check warnings at later stage
2224 new_bitmap->width = new_bitmap->height = 1;
2229 print_timestamp_init("SDLLoadImage");
2231 print_timestamp_time(getBaseNamePtr(filename));
2233 // load image to temporary surface
2234 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2235 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
2237 print_timestamp_time("IMG_Load");
2239 UPDATE_BUSY_STATE();
2241 // create native non-transparent surface for current image
2242 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2243 Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2245 print_timestamp_time("SDLGetNativeSurface (opaque)");
2247 UPDATE_BUSY_STATE();
2249 // set black pixel to transparent if no alpha channel / transparent color
2250 if (!SDLHasAlpha(sdl_image_tmp) &&
2251 !SDLHasColorKey(sdl_image_tmp))
2252 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2253 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2255 // create native transparent surface for current image
2256 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2257 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2259 print_timestamp_time("SDLGetNativeSurface (masked)");
2261 UPDATE_BUSY_STATE();
2263 // free temporary surface
2264 SDL_FreeSurface(sdl_image_tmp);
2266 new_bitmap->width = new_bitmap->surface->w;
2267 new_bitmap->height = new_bitmap->surface->h;
2269 print_timestamp_done("SDLLoadImage");
2275 // ----------------------------------------------------------------------------
2276 // custom cursor fuctions
2277 // ----------------------------------------------------------------------------
2279 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2281 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2282 cursor_info->width, cursor_info->height,
2283 cursor_info->hot_x, cursor_info->hot_y);
2286 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2288 static struct MouseCursorInfo *last_cursor_info = NULL;
2289 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2290 static SDL_Cursor *cursor_default = NULL;
2291 static SDL_Cursor *cursor_current = NULL;
2293 // if invoked for the first time, store the SDL default cursor
2294 if (cursor_default == NULL)
2295 cursor_default = SDL_GetCursor();
2297 // only create new cursor if cursor info (custom only) has changed
2298 if (cursor_info != NULL && cursor_info != last_cursor_info)
2300 cursor_current = create_cursor(cursor_info);
2301 last_cursor_info = cursor_info;
2304 // only set new cursor if cursor info (custom or NULL) has changed
2305 if (cursor_info != last_cursor_info2)
2306 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2308 last_cursor_info2 = cursor_info;
2312 // ============================================================================
2314 // ============================================================================
2316 void SDLOpenAudio(void)
2318 if (program.headless)
2321 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2323 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2327 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2328 AUDIO_NUM_CHANNELS_STEREO,
2329 setup.system.audio_fragment_size) < 0)
2331 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2335 audio.sound_available = TRUE;
2336 audio.music_available = TRUE;
2337 audio.loops_available = TRUE;
2338 audio.sound_enabled = TRUE;
2340 // set number of available mixer channels
2341 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2342 audio.music_channel = MUSIC_CHANNEL;
2343 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2345 Mixer_InitChannels();
2348 void SDLCloseAudio(void)
2351 Mix_HaltChannel(-1);
2354 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2358 // ============================================================================
2360 // ============================================================================
2362 void SDLWaitEvent(Event *event)
2364 SDL_WaitEvent(event);
2367 void SDLCorrectRawMousePosition(int *x, int *y)
2369 if (sdl_renderer == NULL)
2372 // this corrects the raw mouse position for logical screen size within event
2373 // filters (correction done later by SDL library when handling mouse events)
2376 float scale_x, scale_y;
2378 SDL_RenderGetViewport(sdl_renderer, &viewport);
2379 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2381 *x = (int)(*x / scale_x);
2382 *y = (int)(*y / scale_y);
2389 // ============================================================================
2390 // joystick functions
2391 // ============================================================================
2393 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2394 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2395 static int sdl_js_axis[MAX_PLAYERS][2];
2396 static int sdl_js_button[MAX_PLAYERS][2];
2397 static boolean sdl_is_controller[MAX_PLAYERS];
2399 void SDLClearJoystickState(void)
2403 for (i = 0; i < MAX_PLAYERS; i++)
2405 for (j = 0; j < 2; j++)
2407 sdl_js_axis_raw[i][j] = -1;
2408 sdl_js_axis[i][j] = 0;
2409 sdl_js_button[i][j] = 0;
2414 boolean SDLOpenJoystick(int nr)
2416 if (nr < 0 || nr >= MAX_PLAYERS)
2419 sdl_is_controller[nr] = SDL_IsGameController(nr);
2422 Error(ERR_DEBUG, "opening joystick %d (%s)",
2423 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2426 if (sdl_is_controller[nr])
2427 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2429 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2431 return (sdl_joystick[nr] != NULL);
2434 void SDLCloseJoystick(int nr)
2436 if (nr < 0 || nr >= MAX_PLAYERS)
2440 Error(ERR_DEBUG, "closing joystick %d", nr);
2443 if (sdl_is_controller[nr])
2444 SDL_GameControllerClose(sdl_joystick[nr]);
2446 SDL_JoystickClose(sdl_joystick[nr]);
2448 sdl_joystick[nr] = NULL;
2451 boolean SDLCheckJoystickOpened(int nr)
2453 if (nr < 0 || nr >= MAX_PLAYERS)
2456 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2459 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2461 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2462 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2463 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2464 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2466 if (nr < 0 || nr >= MAX_PLAYERS)
2472 // prevent (slightly jittering, but centered) axis A from resetting axis B
2473 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2474 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2477 sdl_js_axis[nr][axis_id] = axis_value;
2478 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2481 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2483 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2484 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2485 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2486 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2487 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2488 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2489 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2490 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2493 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2494 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2495 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2496 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2497 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2498 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2499 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2500 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2502 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2503 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2504 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2505 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2506 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2508 if (nr < 0 || nr >= MAX_PLAYERS)
2511 if (button_id == -1)
2514 sdl_js_button[nr][button_id] = button_state;
2517 void HandleJoystickEvent(Event *event)
2521 case SDL_CONTROLLERDEVICEADDED:
2523 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2524 event->cdevice.which);
2529 case SDL_CONTROLLERDEVICEREMOVED:
2531 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2532 event->cdevice.which);
2537 case SDL_CONTROLLERAXISMOTION:
2539 Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2540 event->caxis.which, event->caxis.axis, event->caxis.value);
2542 setJoystickAxis(event->caxis.which,
2544 event->caxis.value);
2547 case SDL_CONTROLLERBUTTONDOWN:
2549 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2550 event->cbutton.which, event->cbutton.button);
2552 setJoystickButton(event->cbutton.which,
2553 event->cbutton.button,
2557 case SDL_CONTROLLERBUTTONUP:
2559 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2560 event->cbutton.which, event->cbutton.button);
2562 setJoystickButton(event->cbutton.which,
2563 event->cbutton.button,
2567 case SDL_JOYAXISMOTION:
2568 if (sdl_is_controller[event->jaxis.which])
2572 Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2573 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2575 if (event->jaxis.axis < 4)
2576 setJoystickAxis(event->jaxis.which,
2578 event->jaxis.value);
2581 case SDL_JOYBUTTONDOWN:
2582 if (sdl_is_controller[event->jaxis.which])
2586 Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2587 event->jbutton.which, event->jbutton.button);
2589 if (event->jbutton.button < 4)
2590 setJoystickButton(event->jbutton.which,
2591 event->jbutton.button,
2595 case SDL_JOYBUTTONUP:
2596 if (sdl_is_controller[event->jaxis.which])
2600 Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2601 event->jbutton.which, event->jbutton.button);
2603 if (event->jbutton.button < 4)
2604 setJoystickButton(event->jbutton.which,
2605 event->jbutton.button,
2614 void SDLInitJoysticks(void)
2616 static boolean sdl_joystick_subsystem_initialized = FALSE;
2617 boolean print_warning = !sdl_joystick_subsystem_initialized;
2618 char *mappings_file_base = getPath2(options.conf_directory,
2619 GAMECONTROLLER_BASENAME);
2620 char *mappings_file_user = getPath2(getUserGameDataDir(),
2621 GAMECONTROLLER_BASENAME);
2625 if (!sdl_joystick_subsystem_initialized)
2627 sdl_joystick_subsystem_initialized = TRUE;
2629 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2631 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2633 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2637 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2639 // the included game controller base mappings should always be found
2640 if (num_mappings == -1)
2641 Error(ERR_WARN, "no game controller base mappings found");
2644 Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2647 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2650 // the personal game controller user mappings may or may not be found
2651 if (num_mappings == -1)
2652 Error(ERR_WARN, "no game controller user mappings found");
2654 Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2656 Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2659 checked_free(mappings_file_base);
2660 checked_free(mappings_file_user);
2663 for (i = 0; i < SDL_NumJoysticks(); i++)
2665 const char *name, *type;
2667 if (SDL_IsGameController(i))
2669 name = SDL_GameControllerNameForIndex(i);
2670 type = "game controller";
2674 name = SDL_JoystickNameForIndex(i);
2678 Error(ERR_INFO, "- joystick %d (%s): '%s'",
2679 i, type, (name ? name : "(Unknown)"));
2684 // assign joysticks from configured to connected joystick for all players
2685 for (i = 0; i < MAX_PLAYERS; i++)
2687 // get configured joystick for this player
2688 char *device_name = setup.input[i].joy.device_name;
2689 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2691 if (joystick_nr >= SDL_NumJoysticks())
2693 if (setup.input[i].use_joystick && print_warning)
2694 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2699 // store configured joystick number for each player
2700 joystick.nr[i] = joystick_nr;
2703 // now open all connected joysticks (regardless if configured or not)
2704 for (i = 0; i < SDL_NumJoysticks(); i++)
2706 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2707 if (SDLCheckJoystickOpened(i))
2708 SDLCloseJoystick(i);
2710 if (SDLOpenJoystick(i))
2711 joystick.status = JOYSTICK_ACTIVATED;
2712 else if (print_warning)
2713 Error(ERR_WARN, "cannot open joystick %d", i);
2716 SDLClearJoystickState();
2719 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2721 if (nr < 0 || nr >= MAX_PLAYERS)
2725 *x = sdl_js_axis[nr][0];
2727 *y = sdl_js_axis[nr][1];
2730 *b1 = sdl_js_button[nr][0];
2732 *b2 = sdl_js_button[nr][1];
2738 // ============================================================================
2739 // touch input overlay functions
2740 // ============================================================================
2742 #if defined(USE_TOUCH_INPUT_OVERLAY)
2743 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2746 int grid_xsize = overlay.grid_xsize;
2747 int grid_ysize = overlay.grid_ysize;
2750 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2751 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2753 for (x = 0; x < grid_xsize; x++)
2755 rect.x = (x + 0) * video.screen_width / grid_xsize;
2756 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2758 for (y = 0; y < grid_ysize; y++)
2760 rect.y = (y + 0) * video.screen_height / grid_ysize;
2761 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2763 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2764 SDL_RenderDrawRect(sdl_renderer, &rect);
2768 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2771 static void RenderFillRectangle(int x, int y, int width, int height)
2773 SDL_Rect rect = { x, y, width, height };
2775 SDL_RenderFillRect(sdl_renderer, &rect);
2778 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2780 static int alpha_direction = 0;
2781 static int alpha_highlight = 0;
2782 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2783 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2785 int grid_xsize = overlay.grid_xsize;
2786 int grid_ysize = overlay.grid_ysize;
2789 if (alpha == alpha_max)
2791 if (alpha_direction < 0)
2793 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2795 if (alpha_highlight == 0)
2796 alpha_direction = 1;
2800 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2802 if (alpha_highlight == alpha_max)
2803 alpha_direction = -1;
2808 alpha_direction = 1;
2809 alpha_highlight = alpha;
2812 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2814 for (x = 0; x < grid_xsize; x++)
2816 for (y = 0; y < grid_ysize; y++)
2818 int grid_button = overlay.grid_button[x][y];
2819 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2820 int alpha_draw = alpha;
2821 int outline_border = MV_NONE;
2822 int border_size = 2;
2823 boolean draw_outlined = setup.touch.draw_outlined;
2824 boolean draw_pressed = setup.touch.draw_pressed;
2826 if (grid_button == CHAR_GRID_BUTTON_NONE)
2829 if (grid_button == overlay.grid_button_highlight)
2830 alpha_draw = alpha_highlight;
2832 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2835 draw_outlined = FALSE;
2837 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2840 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2842 rect.x = (x + 0) * video.screen_width / grid_xsize;
2843 rect.y = (y + 0) * video.screen_height / grid_ysize;
2844 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2845 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2847 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2849 rect.x += border_size;
2850 rect.w -= border_size;
2852 outline_border |= MV_LEFT;
2855 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2857 rect.w -= border_size;
2859 outline_border |= MV_RIGHT;
2862 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2864 rect.y += border_size;
2865 rect.h -= border_size;
2867 outline_border |= MV_UP;
2870 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2872 rect.h -= border_size;
2874 outline_border |= MV_DOWN;
2879 int rect_x = rect.x +
2880 (outline_border & MV_LEFT ? border_size : 0);
2881 int rect_w = rect.w -
2882 (outline_border & MV_LEFT ? border_size : 0) -
2883 (outline_border & MV_RIGHT ? border_size : 0);
2885 if (outline_border & MV_LEFT)
2886 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2888 if (outline_border & MV_RIGHT)
2889 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2890 border_size, rect.h);
2892 if (outline_border & MV_UP)
2893 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2895 if (outline_border & MV_DOWN)
2896 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
2897 rect_w, border_size);
2901 SDL_RenderFillRect(sdl_renderer, &rect);
2906 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2909 static void DrawTouchInputOverlay(void)
2911 static boolean deactivated = TRUE;
2912 static boolean show_grid = FALSE;
2913 static int alpha = 0;
2914 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2915 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2916 boolean active = (overlay.enabled && overlay.active);
2918 if (!active && deactivated)
2923 if (alpha < alpha_max)
2924 alpha = MIN(alpha + alpha_step, alpha_max);
2926 deactivated = FALSE;
2930 alpha = MAX(0, alpha - alpha_step);
2936 if (overlay.show_grid)
2938 else if (deactivated)
2942 DrawTouchInputOverlay_ShowGrid(alpha);
2944 DrawTouchInputOverlay_ShowGridButtons(alpha);