1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
19 #define ENABLE_UNUSED_CODE 0 // currently unused functions
21 #define DEBUG_JOYSTICKS 0
24 // ============================================================================
26 // ============================================================================
28 // SDL internal variables
29 static SDL_Window *sdl_window = NULL;
30 static SDL_Renderer *sdl_renderer = NULL;
31 static SDL_Texture *sdl_texture_stream = NULL;
32 static SDL_Texture *sdl_texture_target = NULL;
33 static boolean fullscreen_enabled = FALSE;
34 static boolean limit_screen_updates = FALSE;
37 // functions from SGE library
38 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
40 #if defined(USE_TOUCH_INPUT_OVERLAY)
41 // functions to draw overlay graphics for touch device input
42 static void DrawTouchInputOverlay(void);
43 static void DrawTouchGadgetsOverlay(void);
46 void SDLLimitScreenUpdates(boolean enable)
48 limit_screen_updates = enable;
51 static void FinalizeScreen(int draw_target)
53 // copy global animations to render target buffer, if defined (below border)
54 if (gfx.draw_global_anim_function != NULL)
55 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
57 // copy global masked border to render target buffer, if defined
58 if (gfx.draw_global_border_function != NULL)
59 gfx.draw_global_border_function(draw_target);
61 // copy global animations to render target buffer, if defined (above border)
62 if (gfx.draw_global_anim_function != NULL)
63 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
65 // copy tile selection cursor to render target buffer, if defined (above all)
66 if (gfx.draw_tile_cursor_function != NULL)
67 gfx.draw_tile_cursor_function(draw_target);
70 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
72 static unsigned int update_screen_delay = 0;
73 unsigned int update_screen_delay_value = 50; // (milliseconds)
74 SDL_Surface *screen = backbuffer->surface;
76 if (limit_screen_updates &&
77 !DelayReached(&update_screen_delay, update_screen_delay_value))
80 LimitScreenUpdates(FALSE);
84 static int LastFrameCounter = 0;
85 boolean changed = (FrameCounter != LastFrameCounter);
87 printf("::: FrameCounter == %d [%s]\n", FrameCounter,
88 (changed ? "-" : "SAME FRAME UPDATED"));
90 LastFrameCounter = FrameCounter;
99 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
100 gfx.final_screen_bitmap != NULL) // may not be initialized yet
102 // draw global animations using bitmaps instead of using textures
103 // to prevent texture scaling artefacts (this is potentially slower)
105 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
106 gfx.win_xsize, gfx.win_ysize, 0, 0);
108 FinalizeScreen(DRAW_TO_SCREEN);
110 screen = gfx.final_screen_bitmap->surface;
112 // force full window redraw
116 SDL_Texture *sdl_texture = sdl_texture_stream;
118 // deactivate use of target texture if render targets are not supported
119 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
120 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
121 sdl_texture_target == NULL)
122 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
124 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
125 sdl_texture = sdl_texture_target;
129 int bytes_x = screen->pitch / video.width;
130 int bytes_y = screen->pitch;
132 SDL_UpdateTexture(sdl_texture, rect,
133 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
138 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
141 int xoff = video.screen_xoffset;
142 int yoff = video.screen_yoffset;
143 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
144 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
145 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
147 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
148 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
149 dst_rect2 = &dst_rect_screen;
151 dst_rect1 = &dst_rect_screen;
153 #if defined(HAS_SCREEN_KEYBOARD)
154 if (video.shifted_up || video.shifted_up_delay)
156 int time_current = SDL_GetTicks();
157 int pos = video.shifted_up_pos;
158 int pos_last = video.shifted_up_pos_last;
160 if (!DelayReachedExt(&video.shifted_up_delay, video.shifted_up_delay_value,
163 int delay = time_current - video.shifted_up_delay;
164 int delay_value = video.shifted_up_delay_value;
166 pos = pos_last + (pos - pos_last) * delay / delay_value;
170 video.shifted_up_pos_last = pos;
171 video.shifted_up_delay = 0;
174 SDL_Rect src_rect_up = { 0, pos, video.width, video.height - pos };
175 SDL_Rect dst_rect_up = { xoff, yoff, video.width, video.height - pos };
177 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
178 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
180 src_rect2 = &src_rect_up;
181 dst_rect2 = &dst_rect_up;
185 src_rect1 = &src_rect_up;
186 dst_rect1 = &dst_rect_up;
191 // clear render target buffer
192 SDL_RenderClear(sdl_renderer);
194 // set renderer to use target texture for rendering
195 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
196 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
197 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
199 // copy backbuffer texture to render target buffer
200 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
201 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
203 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
204 FinalizeScreen(DRAW_TO_SCREEN);
206 // when using target texture, copy it to screen buffer
207 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
208 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
210 SDL_SetRenderTarget(sdl_renderer, NULL);
211 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
214 #if defined(USE_TOUCH_INPUT_OVERLAY)
215 // draw overlay graphics for touch device input, if needed
216 DrawTouchInputOverlay();
218 // draw overlay gadgets for touch device input, if needed
219 DrawTouchGadgetsOverlay();
222 // global synchronization point of the game to align video frame delay
223 if (with_frame_delay)
224 WaitUntilDelayReached(&video.frame_delay, video.frame_delay_value);
226 video.frame_counter++;
228 // show render target buffer on screen
229 SDL_RenderPresent(sdl_renderer);
232 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
234 PumpEvents(); // execute event filter actions while waiting
236 UpdateScreenExt(rect, TRUE);
239 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
241 UpdateScreenExt(rect, FALSE);
244 void Delay_WithScreenUpdates(unsigned int delay)
246 unsigned int time_current = SDL_GetTicks();
247 unsigned int time_delayed = time_current + delay;
249 while (time_current < time_delayed)
251 // updating the screen contains waiting for frame delay (non-busy)
252 UpdateScreen_WithFrameDelay(NULL);
254 time_current = SDL_GetTicks();
258 static void SDLSetWindowIcon(char *basename)
260 // (setting the window icon on Mac OS X would replace the high-quality
261 // dock icon with the currently smaller (and uglier) icon from file)
263 #if !defined(PLATFORM_MACOSX)
264 char *filename = getCustomImageFilename(basename);
265 SDL_Surface *surface;
267 if (filename == NULL)
269 Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
274 if ((surface = IMG_Load(filename)) == NULL)
276 Error(ERR_WARN, "IMG_Load() failed: %s", SDL_GetError());
281 // set transparent color
282 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
283 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
285 SDL_SetWindowIcon(sdl_window, surface);
289 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
290 SDL_PixelFormat *format2)
292 return (format1->format == format2->format &&
293 format1->BitsPerPixel == format2->BitsPerPixel &&
294 format1->BytesPerPixel == format2->BytesPerPixel &&
295 format1->Rmask == format2->Rmask &&
296 format1->Gmask == format2->Gmask &&
297 format1->Bmask == format2->Bmask);
300 static Pixel SDLGetColorKey(SDL_Surface *surface)
304 if (SDL_GetColorKey(surface, &color_key) != 0)
310 static boolean SDLHasColorKey(SDL_Surface *surface)
312 return (SDLGetColorKey(surface) != -1);
315 static boolean SDLHasAlpha(SDL_Surface *surface)
317 SDL_BlendMode blend_mode;
319 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
322 return (blend_mode == SDL_BLENDMODE_BLEND);
325 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
327 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
329 SDL_SetSurfaceBlendMode(surface, blend_mode);
330 SDL_SetSurfaceAlphaMod(surface, alpha);
333 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
335 SDL_PixelFormat format;
336 SDL_Surface *new_surface;
341 if (backbuffer && backbuffer->surface)
343 format = *backbuffer->surface->format;
344 format.Amask = surface->format->Amask; // keep alpha channel
348 format = *surface->format;
351 new_surface = SDL_ConvertSurface(surface, &format, 0);
353 if (new_surface == NULL)
354 Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
359 boolean SDLSetNativeSurface(SDL_Surface **surface)
361 SDL_Surface *new_surface;
363 if (surface == NULL ||
365 backbuffer == NULL ||
366 backbuffer->surface == NULL)
369 // if pixel format already optimized for destination surface, do nothing
370 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
373 new_surface = SDLGetNativeSurface(*surface);
375 SDL_FreeSurface(*surface);
377 *surface = new_surface;
382 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
384 if (program.headless)
387 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
390 Error(ERR_EXIT, "SDL_CreateTextureFromSurface() failed: %s",
396 void SDLCreateBitmapTextures(Bitmap *bitmap)
402 SDL_DestroyTexture(bitmap->texture);
403 if (bitmap->texture_masked)
404 SDL_DestroyTexture(bitmap->texture_masked);
406 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
407 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
410 void SDLFreeBitmapTextures(Bitmap *bitmap)
416 SDL_DestroyTexture(bitmap->texture);
417 if (bitmap->texture_masked)
418 SDL_DestroyTexture(bitmap->texture_masked);
420 bitmap->texture = NULL;
421 bitmap->texture_masked = NULL;
424 void SDLInitVideoDisplay(void)
426 // initialize SDL video
427 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
428 Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
430 // set default SDL depth
431 video.default_depth = 32; // (how to determine video depth in SDL2?)
433 // Code used with SDL 1.2:
434 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
437 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
439 if (program.headless)
442 video.window_scaling_percent = setup.window_scaling_percent;
443 video.window_scaling_quality = setup.window_scaling_quality;
445 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
447 // SDL 2.0: support for (desktop) fullscreen mode available
448 video.fullscreen_available = TRUE;
450 // open SDL video output device (window or fullscreen mode)
451 if (!SDLSetVideoMode(fullscreen))
452 Error(ERR_EXIT, "setting video mode failed");
454 // !!! SDL2 can only set the window icon if the window already exists !!!
456 SDLSetWindowIcon(program.icon_filename);
458 // set window and icon title
462 static void SDLInitVideoBuffer_DrawBuffer(void)
464 /* SDL cannot directly draw to the visible video framebuffer like X11,
465 but always uses a backbuffer, which is then blitted to the visible
466 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
467 visible video framebuffer with 'SDL_Flip', if the hardware supports
468 this). Therefore do not use an additional backbuffer for drawing, but
469 use a symbolic buffer (distinguishable from the SDL backbuffer) called
470 'window', which indicates that the SDL backbuffer should be updated to
471 the visible video framebuffer when attempting to blit to it.
473 For convenience, it seems to be a good idea to create this symbolic
474 buffer 'window' at the same size as the SDL backbuffer. Although it
475 should never be drawn to directly, it would do no harm nevertheless. */
477 // create additional (symbolic) buffer for double-buffering
478 ReCreateBitmap(&window, video.width, video.height);
480 // create dummy drawing buffer for headless mode, if needed
481 if (program.headless)
482 ReCreateBitmap(&backbuffer, video.width, video.height);
485 void SDLInitVideoBuffer(boolean fullscreen)
487 SDLInitVideoBuffer_VideoBuffer(fullscreen);
488 SDLInitVideoBuffer_DrawBuffer();
491 static boolean SDLCreateScreen(boolean fullscreen)
493 SDL_Surface *new_surface = NULL;
495 int surface_flags_window = SURFACE_FLAGS | SDL_WINDOW_RESIZABLE;
496 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
499 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
501 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
502 _without_ enabling 2D/3D acceleration and/or guest additions installed,
503 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
504 it will try to use accelerated graphics and apparently fails miserably) */
505 int renderer_flags = SDL_RENDERER_SOFTWARE;
508 SDLSetScreenSizeAndOffsets(video.width, video.height);
510 int width = video.width;
511 int height = video.height;
512 int screen_width = video.screen_width;
513 int screen_height = video.screen_height;
514 int surface_flags = (fullscreen ? surface_flags_fullscreen :
515 surface_flags_window);
517 // default window size is unscaled
518 video.window_width = screen_width;
519 video.window_height = screen_height;
521 // store if initial screen mode is fullscreen mode when changing screen size
522 video.fullscreen_initial = fullscreen;
524 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
526 video.window_width = window_scaling_factor * screen_width;
527 video.window_height = window_scaling_factor * screen_height;
529 if (sdl_texture_stream)
531 SDL_DestroyTexture(sdl_texture_stream);
532 sdl_texture_stream = NULL;
535 if (sdl_texture_target)
537 SDL_DestroyTexture(sdl_texture_target);
538 sdl_texture_target = NULL;
541 if (!(fullscreen && fullscreen_enabled))
545 SDL_DestroyRenderer(sdl_renderer);
551 SDL_DestroyWindow(sdl_window);
556 if (sdl_window == NULL)
557 sdl_window = SDL_CreateWindow(program.window_title,
558 SDL_WINDOWPOS_CENTERED,
559 SDL_WINDOWPOS_CENTERED,
564 if (sdl_window != NULL)
566 if (sdl_renderer == NULL)
567 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
569 if (sdl_renderer != NULL)
571 SDL_RenderSetLogicalSize(sdl_renderer, screen_width, screen_height);
572 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
573 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
575 SDLSetScreenVsyncMode(setup.vsync_mode);
577 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
578 SDL_PIXELFORMAT_ARGB8888,
579 SDL_TEXTUREACCESS_STREAMING,
582 if (SDL_RenderTargetSupported(sdl_renderer))
583 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
584 SDL_PIXELFORMAT_ARGB8888,
585 SDL_TEXTUREACCESS_TARGET,
588 if (sdl_texture_stream != NULL)
590 // use SDL default values for RGB masks and no alpha channel
591 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
593 if (new_surface == NULL)
594 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
598 Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
603 Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
608 Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
611 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
612 if (new_surface != NULL)
613 fullscreen_enabled = fullscreen;
615 if (backbuffer == NULL)
616 backbuffer = CreateBitmapStruct();
618 backbuffer->width = video.width;
619 backbuffer->height = video.height;
621 if (backbuffer->surface)
622 SDL_FreeSurface(backbuffer->surface);
624 backbuffer->surface = new_surface;
626 return (new_surface != NULL);
629 boolean SDLSetVideoMode(boolean fullscreen)
631 boolean success = FALSE;
635 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
637 // switch display to fullscreen mode, if available
638 success = SDLCreateScreen(TRUE);
642 // switching display to fullscreen mode failed -- do not try it again
643 video.fullscreen_available = FALSE;
647 video.fullscreen_enabled = TRUE;
651 if ((!fullscreen && video.fullscreen_enabled) || !success)
653 // switch display to window mode
654 success = SDLCreateScreen(FALSE);
658 // switching display to window mode failed -- should not happen
662 video.fullscreen_enabled = FALSE;
663 video.window_scaling_percent = setup.window_scaling_percent;
664 video.window_scaling_quality = setup.window_scaling_quality;
666 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
670 SDLRedrawWindow(); // map window
675 void SDLSetWindowTitle(void)
677 if (sdl_window == NULL)
680 SDL_SetWindowTitle(sdl_window, program.window_title);
683 void SDLSetWindowScaling(int window_scaling_percent)
685 if (sdl_window == NULL)
688 float window_scaling_factor = (float)window_scaling_percent / 100;
689 int new_window_width = (int)(window_scaling_factor * video.screen_width);
690 int new_window_height = (int)(window_scaling_factor * video.screen_height);
692 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
694 video.window_scaling_percent = window_scaling_percent;
695 video.window_width = new_window_width;
696 video.window_height = new_window_height;
701 void SDLSetWindowScalingQuality(char *window_scaling_quality)
703 SDL_Texture *new_texture;
705 if (sdl_texture_stream == NULL)
708 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
710 new_texture = SDL_CreateTexture(sdl_renderer,
711 SDL_PIXELFORMAT_ARGB8888,
712 SDL_TEXTUREACCESS_STREAMING,
713 video.width, video.height);
715 if (new_texture != NULL)
717 SDL_DestroyTexture(sdl_texture_stream);
719 sdl_texture_stream = new_texture;
722 if (SDL_RenderTargetSupported(sdl_renderer))
723 new_texture = SDL_CreateTexture(sdl_renderer,
724 SDL_PIXELFORMAT_ARGB8888,
725 SDL_TEXTUREACCESS_TARGET,
726 video.width, video.height);
730 if (new_texture != NULL)
732 SDL_DestroyTexture(sdl_texture_target);
734 sdl_texture_target = new_texture;
739 video.window_scaling_quality = window_scaling_quality;
742 void SDLSetWindowFullscreen(boolean fullscreen)
744 if (sdl_window == NULL)
747 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
749 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
750 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
752 // if screen size was changed in fullscreen mode, correct desktop window size
753 if (!fullscreen && video.fullscreen_initial)
755 SDLSetWindowScaling(setup.window_scaling_percent);
756 SDL_SetWindowPosition(sdl_window,
757 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
759 video.fullscreen_initial = FALSE;
763 void SDLSetDisplaySize(void)
765 SDL_Rect display_bounds;
767 SDL_GetDisplayBounds(0, &display_bounds);
769 video.display_width = display_bounds.w;
770 video.display_height = display_bounds.h;
773 Error(ERR_DEBUG, "SDL real screen size: %d x %d",
774 video.display_width, video.display_height);
778 void SDLSetScreenSizeAndOffsets(int width, int height)
780 // set default video screen size and offsets
781 video.screen_width = width;
782 video.screen_height = height;
783 video.screen_xoffset = 0;
784 video.screen_yoffset = 0;
786 #if defined(USE_COMPLETE_DISPLAY)
787 float ratio_video = (float) width / height;
788 float ratio_display = (float) video.display_width / video.display_height;
790 if (ratio_video != ratio_display)
792 // adjust drawable screen size to cover the whole device display
794 if (ratio_video < ratio_display)
795 video.screen_width *= ratio_display / ratio_video;
797 video.screen_height *= ratio_video / ratio_display;
799 video.screen_xoffset = (video.screen_width - width) / 2;
800 video.screen_yoffset = (video.screen_height - height) / 2;
803 Error(ERR_DEBUG, "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
805 video.screen_width, video.screen_height,
806 ratio_video, ratio_display);
812 void SDLSetScreenSizeForRenderer(int width, int height)
814 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
817 void SDLSetScreenProperties(void)
819 SDLSetScreenSizeAndOffsets(video.width, video.height);
820 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
823 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
825 video.screen_rendering_mode =
826 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
827 SPECIAL_RENDERING_BITMAP :
828 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
829 SPECIAL_RENDERING_TARGET:
830 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
831 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
834 void SDLSetScreenVsyncMode(char *vsync_mode)
837 (strEqual(vsync_mode, STR_VSYNC_MODE_NORMAL) ? VSYNC_MODE_NORMAL :
838 strEqual(vsync_mode, STR_VSYNC_MODE_ADAPTIVE) ? VSYNC_MODE_ADAPTIVE :
840 int result = SDL_GL_SetSwapInterval(interval);
842 // if adaptive vsync requested, but not supported, retry with normal vsync
843 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
844 SDL_GL_SetSwapInterval(VSYNC_MODE_NORMAL);
847 void SDLRedrawWindow(void)
849 UpdateScreen_WithoutFrameDelay(NULL);
852 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
855 if (program.headless)
858 SDL_Surface *surface =
859 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
862 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
864 SDLSetNativeSurface(&surface);
866 bitmap->surface = surface;
869 void SDLFreeBitmapPointers(Bitmap *bitmap)
872 SDL_FreeSurface(bitmap->surface);
873 if (bitmap->surface_masked)
874 SDL_FreeSurface(bitmap->surface_masked);
876 bitmap->surface = NULL;
877 bitmap->surface_masked = NULL;
880 SDL_DestroyTexture(bitmap->texture);
881 if (bitmap->texture_masked)
882 SDL_DestroyTexture(bitmap->texture_masked);
884 bitmap->texture = NULL;
885 bitmap->texture_masked = NULL;
888 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
889 int src_x, int src_y, int width, int height,
890 int dst_x, int dst_y, int mask_mode)
892 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
893 SDL_Rect src_rect, dst_rect;
905 // if (src_bitmap != backbuffer || dst_bitmap != window)
906 if (!(src_bitmap == backbuffer && dst_bitmap == window))
907 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
908 src_bitmap->surface_masked : src_bitmap->surface),
909 &src_rect, real_dst_bitmap->surface, &dst_rect);
911 if (dst_bitmap == window)
912 UpdateScreen_WithFrameDelay(&dst_rect);
915 void SDLBlitTexture(Bitmap *bitmap,
916 int src_x, int src_y, int width, int height,
917 int dst_x, int dst_y, int mask_mode)
919 SDL_Texture *texture;
924 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
939 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
942 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
945 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
953 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
955 if (dst_bitmap == window)
956 UpdateScreen_WithFrameDelay(&rect);
959 void PrepareFadeBitmap(int draw_target)
961 Bitmap *fade_bitmap =
962 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
963 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
965 if (fade_bitmap == NULL)
968 // copy backbuffer to fading buffer
969 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
971 // add border and animations to fading buffer
972 FinalizeScreen(draw_target);
975 void SDLFadeRectangle(int x, int y, int width, int height,
976 int fade_mode, int fade_delay, int post_delay,
977 void (*draw_border_function)(void))
979 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
980 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
981 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
982 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
983 SDL_Surface *surface_screen = backbuffer->surface;
984 SDL_Rect src_rect, dst_rect;
986 int src_x = x, src_y = y;
987 int dst_x = x, dst_y = y;
988 unsigned int time_last, time_current;
990 // store function for drawing global masked border
991 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
993 // deactivate drawing of global border while fading, if needed
994 if (draw_border_function == NULL)
995 gfx.draw_global_border_function = NULL;
1000 src_rect.h = height;
1004 dst_rect.w = width; // (ignored)
1005 dst_rect.h = height; // (ignored)
1007 dst_rect2 = dst_rect;
1009 // before fading in, store backbuffer (without animation graphics)
1010 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1011 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1013 // copy source and target surfaces to temporary surfaces for fading
1014 if (fade_mode & FADE_TYPE_TRANSFORM)
1016 // (source and target fading buffer already prepared)
1018 else if (fade_mode & FADE_TYPE_FADE_IN)
1020 // (target fading buffer already prepared)
1021 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1023 else // FADE_TYPE_FADE_OUT
1025 // (source fading buffer already prepared)
1026 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1029 time_current = SDL_GetTicks();
1031 if (fade_mode == FADE_MODE_MELT)
1033 boolean done = FALSE;
1034 int melt_pixels = 2;
1035 int melt_columns = width / melt_pixels;
1036 int ypos[melt_columns];
1037 int max_steps = height / 8 + 32;
1042 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1044 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1046 ypos[0] = -GetSimpleRandom(16);
1048 for (i = 1 ; i < melt_columns; i++)
1050 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1052 ypos[i] = ypos[i - 1] + r;
1065 time_last = time_current;
1066 time_current = SDL_GetTicks();
1067 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1068 steps_final = MIN(MAX(0, steps), max_steps);
1072 done = (steps_done >= steps_final);
1074 for (i = 0 ; i < melt_columns; i++)
1082 else if (ypos[i] < height)
1087 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1089 if (ypos[i] + dy >= height)
1090 dy = height - ypos[i];
1092 // copy part of (appearing) target surface to upper area
1093 src_rect.x = src_x + i * melt_pixels;
1094 // src_rect.y = src_y + ypos[i];
1096 src_rect.w = melt_pixels;
1098 src_rect.h = ypos[i] + dy;
1100 dst_rect.x = dst_x + i * melt_pixels;
1101 // dst_rect.y = dst_y + ypos[i];
1104 if (steps_done >= steps_final)
1105 SDL_BlitSurface(surface_target, &src_rect,
1106 surface_screen, &dst_rect);
1110 // copy part of (disappearing) source surface to lower area
1111 src_rect.x = src_x + i * melt_pixels;
1113 src_rect.w = melt_pixels;
1114 src_rect.h = height - ypos[i];
1116 dst_rect.x = dst_x + i * melt_pixels;
1117 dst_rect.y = dst_y + ypos[i];
1119 if (steps_done >= steps_final)
1120 SDL_BlitSurface(surface_source, &src_rect,
1121 surface_screen, &dst_rect);
1127 src_rect.x = src_x + i * melt_pixels;
1129 src_rect.w = melt_pixels;
1130 src_rect.h = height;
1132 dst_rect.x = dst_x + i * melt_pixels;
1135 if (steps_done >= steps_final)
1136 SDL_BlitSurface(surface_target, &src_rect,
1137 surface_screen, &dst_rect);
1141 if (steps_done >= steps_final)
1143 if (draw_border_function != NULL)
1144 draw_border_function();
1146 UpdateScreen_WithFrameDelay(&dst_rect2);
1150 else if (fade_mode == FADE_MODE_CURTAIN)
1154 int xx_size = width / 2;
1156 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1158 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1160 for (xx = 0; xx < xx_size;)
1162 time_last = time_current;
1163 time_current = SDL_GetTicks();
1164 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1165 xx_final = MIN(MAX(0, xx), xx_size);
1170 src_rect.h = height;
1175 // draw new (target) image to screen buffer
1176 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1178 if (xx_final < xx_size)
1180 src_rect.w = xx_size - xx_final;
1181 src_rect.h = height;
1183 // draw old (source) image to screen buffer (left side)
1185 src_rect.x = src_x + xx_final;
1188 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1190 // draw old (source) image to screen buffer (right side)
1192 src_rect.x = src_x + xx_size;
1193 dst_rect.x = dst_x + xx_size + xx_final;
1195 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1198 if (draw_border_function != NULL)
1199 draw_border_function();
1201 // only update the region of the screen that is affected from fading
1202 UpdateScreen_WithFrameDelay(&dst_rect2);
1205 else // fading in, fading out or cross-fading
1210 for (alpha = 0.0; alpha < 255.0;)
1212 time_last = time_current;
1213 time_current = SDL_GetTicks();
1214 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1215 alpha_final = MIN(MAX(0, alpha), 255);
1217 // draw existing (source) image to screen buffer
1218 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1220 // draw new (target) image to screen buffer using alpha blending
1221 SDLSetAlpha(surface_target, TRUE, alpha_final);
1222 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1224 if (draw_border_function != NULL)
1225 draw_border_function();
1227 // only update the region of the screen that is affected from fading
1228 UpdateScreen_WithFrameDelay(&dst_rect);
1233 Delay_WithScreenUpdates(post_delay);
1235 // restore function for drawing global masked border
1236 gfx.draw_global_border_function = draw_global_border_function;
1238 // after fading in, restore backbuffer (without animation graphics)
1239 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1240 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1243 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1244 int to_x, int to_y, Uint32 color)
1246 SDL_Surface *surface = dst_bitmap->surface;
1250 swap_numbers(&from_x, &to_x);
1253 swap_numbers(&from_y, &to_y);
1257 rect.w = (to_x - from_x + 1);
1258 rect.h = (to_y - from_y + 1);
1260 SDL_FillRect(surface, &rect, color);
1263 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1264 int to_x, int to_y, Uint32 color)
1266 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1269 #if ENABLE_UNUSED_CODE
1270 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1271 int num_points, Uint32 color)
1276 for (i = 0; i < num_points - 1; i++)
1278 for (x = 0; x < line_width; x++)
1280 for (y = 0; y < line_width; y++)
1282 int dx = x - line_width / 2;
1283 int dy = y - line_width / 2;
1285 if ((x == 0 && y == 0) ||
1286 (x == 0 && y == line_width - 1) ||
1287 (x == line_width - 1 && y == 0) ||
1288 (x == line_width - 1 && y == line_width - 1))
1291 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1292 points[i+1].x + dx, points[i+1].y + dy, color);
1299 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1301 SDL_Surface *surface = src_bitmap->surface;
1303 switch (surface->format->BytesPerPixel)
1305 case 1: // assuming 8-bpp
1307 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1311 case 2: // probably 15-bpp or 16-bpp
1313 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1317 case 3: // slow 24-bpp mode; usually not used
1320 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1324 shift = surface->format->Rshift;
1325 color |= *(pix + shift / 8) >> shift;
1326 shift = surface->format->Gshift;
1327 color |= *(pix + shift / 8) >> shift;
1328 shift = surface->format->Bshift;
1329 color |= *(pix + shift / 8) >> shift;
1335 case 4: // probably 32-bpp
1337 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1346 // ============================================================================
1347 // The following functions were taken from the SGE library
1348 // (SDL Graphics Extension Library) by Anders Lindström
1349 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1350 // ============================================================================
1352 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1354 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1356 switch (surface->format->BytesPerPixel)
1361 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1367 // Probably 15-bpp or 16-bpp
1368 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1374 // Slow 24-bpp mode, usually not used
1378 // Gack - slow, but endian correct
1379 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1380 shift = surface->format->Rshift;
1381 *(pix+shift/8) = color>>shift;
1382 shift = surface->format->Gshift;
1383 *(pix+shift/8) = color>>shift;
1384 shift = surface->format->Bshift;
1385 *(pix+shift/8) = color>>shift;
1392 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1400 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1401 Uint8 R, Uint8 G, Uint8 B)
1403 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1406 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1408 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1411 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1413 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1416 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1421 // Gack - slow, but endian correct
1422 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1423 shift = surface->format->Rshift;
1424 *(pix+shift/8) = color>>shift;
1425 shift = surface->format->Gshift;
1426 *(pix+shift/8) = color>>shift;
1427 shift = surface->format->Bshift;
1428 *(pix+shift/8) = color>>shift;
1431 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1433 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1436 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1438 switch (dest->format->BytesPerPixel)
1441 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1445 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1449 _PutPixel24(dest,x,y,color);
1453 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1459 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1461 if (SDL_MUSTLOCK(surface))
1463 if (SDL_LockSurface(surface) < 0)
1469 _PutPixel(surface, x, y, color);
1471 if (SDL_MUSTLOCK(surface))
1473 SDL_UnlockSurface(surface);
1478 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1479 Uint8 r, Uint8 g, Uint8 b)
1481 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1484 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1486 if (y >= 0 && y <= dest->h - 1)
1488 switch (dest->format->BytesPerPixel)
1491 return y*dest->pitch;
1495 return y*dest->pitch/2;
1499 return y*dest->pitch;
1503 return y*dest->pitch/4;
1511 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1514 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1516 switch (surface->format->BytesPerPixel)
1521 *((Uint8 *)surface->pixels + ypitch + x) = color;
1527 // Probably 15-bpp or 16-bpp
1528 *((Uint16 *)surface->pixels + ypitch + x) = color;
1534 // Slow 24-bpp mode, usually not used
1538 // Gack - slow, but endian correct
1539 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1540 shift = surface->format->Rshift;
1541 *(pix+shift/8) = color>>shift;
1542 shift = surface->format->Gshift;
1543 *(pix+shift/8) = color>>shift;
1544 shift = surface->format->Bshift;
1545 *(pix+shift/8) = color>>shift;
1552 *((Uint32 *)surface->pixels + ypitch + x) = color;
1559 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1564 if (SDL_MUSTLOCK(Surface))
1566 if (SDL_LockSurface(Surface) < 0)
1580 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1584 if (x2 > Surface->w - 1)
1585 x2 = Surface->w - 1;
1592 SDL_FillRect(Surface, &l, Color);
1594 if (SDL_MUSTLOCK(Surface))
1596 SDL_UnlockSurface(Surface);
1600 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1601 Uint8 R, Uint8 G, Uint8 B)
1603 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1606 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1619 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1623 if (x2 > Surface->w - 1)
1624 x2 = Surface->w - 1;
1631 SDL_FillRect(Surface, &l, Color);
1634 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1639 if (SDL_MUSTLOCK(Surface))
1641 if (SDL_LockSurface(Surface) < 0)
1655 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1659 if (y2 > Surface->h - 1)
1660 y2 = Surface->h - 1;
1667 SDL_FillRect(Surface, &l, Color);
1669 if (SDL_MUSTLOCK(Surface))
1671 SDL_UnlockSurface(Surface);
1675 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1676 Uint8 R, Uint8 G, Uint8 B)
1678 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1681 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1694 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1698 if (y2 > Surface->h - 1)
1699 y2 = Surface->h - 1;
1706 SDL_FillRect(Surface, &l, Color);
1710 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1711 Sint16 x2, Sint16 y2, Uint32 Color,
1712 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1715 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1720 sdx = (dx < 0) ? -1 : 1;
1721 sdy = (dy < 0) ? -1 : 1;
1733 for (x = 0; x < dx; x++)
1735 Callback(Surface, px, py, Color);
1749 for (y = 0; y < dy; y++)
1751 Callback(Surface, px, py, Color);
1766 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1767 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1768 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1771 sge_DoLine(Surface, X1, Y1, X2, Y2,
1772 SDL_MapRGB(Surface->format, R, G, B), Callback);
1776 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1779 if (SDL_MUSTLOCK(Surface))
1781 if (SDL_LockSurface(Surface) < 0)
1786 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1788 // unlock the display
1789 if (SDL_MUSTLOCK(Surface))
1791 SDL_UnlockSurface(Surface);
1796 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1797 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1799 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1803 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1805 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1809 // ----------------------------------------------------------------------------
1810 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1811 // ----------------------------------------------------------------------------
1813 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1814 int width, int height, Uint32 color)
1818 for (y = src_y; y < src_y + height; y++)
1820 for (x = src_x; x < src_x + width; x++)
1822 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1824 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1829 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1830 int src_x, int src_y, int width, int height,
1831 int dst_x, int dst_y)
1835 for (y = 0; y < height; y++)
1837 for (x = 0; x < width; x++)
1839 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1841 if (pixel != BLACK_PIXEL)
1842 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1848 // ============================================================================
1849 // The following functions were taken from the SDL_gfx library version 2.0.3
1850 // (Rotozoomer) by Andreas Schiffler
1851 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1852 // ============================================================================
1854 // ----------------------------------------------------------------------------
1857 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1858 // ----------------------------------------------------------------------------
1868 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1871 tColorRGBA *sp, *csp, *dp;
1875 sp = csp = (tColorRGBA *) src->pixels;
1876 dp = (tColorRGBA *) dst->pixels;
1877 dgap = dst->pitch - dst->w * 4;
1879 for (y = 0; y < dst->h; y++)
1883 for (x = 0; x < dst->w; x++)
1885 tColorRGBA *sp0 = sp;
1886 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1887 tColorRGBA *sp00 = &sp0[0];
1888 tColorRGBA *sp01 = &sp0[1];
1889 tColorRGBA *sp10 = &sp1[0];
1890 tColorRGBA *sp11 = &sp1[1];
1893 // create new color pixel from all four source color pixels
1894 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1895 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1896 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1897 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1902 // advance source pointers
1905 // advance destination pointer
1909 // advance source pointer
1910 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1912 // advance destination pointers
1913 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1919 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1921 int x, y, *sax, *say, *csax, *csay;
1923 tColorRGBA *sp, *csp, *csp0, *dp;
1926 // use specialized zoom function when scaling down to exactly half size
1927 if (src->w == 2 * dst->w &&
1928 src->h == 2 * dst->h)
1929 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1932 sx = (float) src->w / (float) dst->w;
1933 sy = (float) src->h / (float) dst->h;
1935 // allocate memory for row increments
1936 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1937 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1939 // precalculate row increments
1940 for (x = 0; x <= dst->w; x++)
1941 *csax++ = (int)(sx * x);
1943 for (y = 0; y <= dst->h; y++)
1944 *csay++ = (int)(sy * y);
1947 sp = csp = csp0 = (tColorRGBA *) src->pixels;
1948 dp = (tColorRGBA *) dst->pixels;
1949 dgap = dst->pitch - dst->w * 4;
1952 for (y = 0; y < dst->h; y++)
1957 for (x = 0; x < dst->w; x++)
1962 // advance source pointers
1966 // advance destination pointer
1970 // advance source pointer
1972 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
1974 // advance destination pointers
1975 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1984 // ----------------------------------------------------------------------------
1987 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
1988 // ----------------------------------------------------------------------------
1990 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
1992 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1993 Uint8 *sp, *dp, *csp;
1997 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
1998 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2000 // allocate memory for row increments
2001 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2002 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2004 // precalculate row increments
2007 for (x = 0; x < dst->w; x++)
2010 *csax = (csx >> 16);
2017 for (y = 0; y < dst->h; y++)
2020 *csay = (csy >> 16);
2027 for (x = 0; x < dst->w; x++)
2035 for (y = 0; y < dst->h; y++)
2042 sp = csp = (Uint8 *) src->pixels;
2043 dp = (Uint8 *) dst->pixels;
2044 dgap = dst->pitch - dst->w;
2048 for (y = 0; y < dst->h; y++)
2052 for (x = 0; x < dst->w; x++)
2057 // advance source pointers
2061 // advance destination pointer
2065 // advance source pointer (for row)
2066 csp += ((*csay) * src->pitch);
2069 // advance destination pointers
2079 // ----------------------------------------------------------------------------
2082 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2083 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2084 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2085 // into a 32bit RGBA format on the fly.
2086 // ----------------------------------------------------------------------------
2088 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2090 SDL_Surface *zoom_src = NULL;
2091 SDL_Surface *zoom_dst = NULL;
2092 boolean is_converted = FALSE;
2099 // determine if source surface is 32 bit or 8 bit
2100 is_32bit = (src->format->BitsPerPixel == 32);
2102 if (is_32bit || src->format->BitsPerPixel == 8)
2104 // use source surface 'as is'
2109 // new source surface is 32 bit with a defined RGB ordering
2110 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2111 0x000000ff, 0x0000ff00, 0x00ff0000,
2112 (src->format->Amask ? 0xff000000 : 0));
2113 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2115 is_converted = TRUE;
2118 // allocate surface to completely contain the zoomed surface
2121 // target surface is 32 bit with source RGBA/ABGR ordering
2122 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2123 zoom_src->format->Rmask,
2124 zoom_src->format->Gmask,
2125 zoom_src->format->Bmask,
2126 zoom_src->format->Amask);
2130 // target surface is 8 bit
2131 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2135 // lock source surface
2136 SDL_LockSurface(zoom_src);
2138 // check which kind of surface we have
2141 // call the 32 bit transformation routine to do the zooming
2142 zoomSurfaceRGBA(zoom_src, zoom_dst);
2147 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2148 zoom_dst->format->palette->colors[i] =
2149 zoom_src->format->palette->colors[i];
2150 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2152 // call the 8 bit transformation routine to do the zooming
2153 zoomSurfaceY(zoom_src, zoom_dst);
2156 // unlock source surface
2157 SDL_UnlockSurface(zoom_src);
2159 // free temporary surface
2161 SDL_FreeSurface(zoom_src);
2163 // return destination surface
2167 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2169 SDL_Surface *new_surface;
2171 if (surface == NULL)
2174 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2175 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2177 // remove alpha channel from native non-transparent surface, if defined
2178 SDLSetAlpha(new_surface, FALSE, 0);
2180 // remove transparent color from native non-transparent surface, if defined
2181 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2186 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2188 Bitmap *dst_bitmap = CreateBitmapStruct();
2189 SDL_Surface *src_surface = src_bitmap->surface_masked;
2190 SDL_Surface *dst_surface;
2192 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2193 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2195 dst_bitmap->width = dst_width;
2196 dst_bitmap->height = dst_height;
2198 // create zoomed temporary surface from source surface
2199 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2201 // create native format destination surface from zoomed temporary surface
2202 SDLSetNativeSurface(&dst_surface);
2204 // set color key for zoomed surface from source surface, if defined
2205 if (SDLHasColorKey(src_surface))
2206 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2207 SDLGetColorKey(src_surface));
2209 // create native non-transparent surface for opaque blitting
2210 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2212 // set native transparent surface for masked blitting
2213 dst_bitmap->surface_masked = dst_surface;
2219 // ============================================================================
2220 // load image to bitmap
2221 // ============================================================================
2223 Bitmap *SDLLoadImage(char *filename)
2225 Bitmap *new_bitmap = CreateBitmapStruct();
2226 SDL_Surface *sdl_image_tmp;
2228 if (program.headless)
2230 // prevent sanity check warnings at later stage
2231 new_bitmap->width = new_bitmap->height = 1;
2236 print_timestamp_init("SDLLoadImage");
2238 print_timestamp_time(getBaseNamePtr(filename));
2240 // load image to temporary surface
2241 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2242 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
2244 print_timestamp_time("IMG_Load");
2246 UPDATE_BUSY_STATE();
2248 // create native non-transparent surface for current image
2249 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2250 Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2252 print_timestamp_time("SDLGetNativeSurface (opaque)");
2254 UPDATE_BUSY_STATE();
2256 // set black pixel to transparent if no alpha channel / transparent color
2257 if (!SDLHasAlpha(sdl_image_tmp) &&
2258 !SDLHasColorKey(sdl_image_tmp))
2259 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2260 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2262 // create native transparent surface for current image
2263 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2264 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2266 print_timestamp_time("SDLGetNativeSurface (masked)");
2268 UPDATE_BUSY_STATE();
2270 // free temporary surface
2271 SDL_FreeSurface(sdl_image_tmp);
2273 new_bitmap->width = new_bitmap->surface->w;
2274 new_bitmap->height = new_bitmap->surface->h;
2276 print_timestamp_done("SDLLoadImage");
2282 // ----------------------------------------------------------------------------
2283 // custom cursor fuctions
2284 // ----------------------------------------------------------------------------
2286 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2288 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2289 cursor_info->width, cursor_info->height,
2290 cursor_info->hot_x, cursor_info->hot_y);
2293 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2295 static struct MouseCursorInfo *last_cursor_info = NULL;
2296 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2297 static SDL_Cursor *cursor_default = NULL;
2298 static SDL_Cursor *cursor_current = NULL;
2300 // if invoked for the first time, store the SDL default cursor
2301 if (cursor_default == NULL)
2302 cursor_default = SDL_GetCursor();
2304 // only create new cursor if cursor info (custom only) has changed
2305 if (cursor_info != NULL && cursor_info != last_cursor_info)
2307 cursor_current = create_cursor(cursor_info);
2308 last_cursor_info = cursor_info;
2311 // only set new cursor if cursor info (custom or NULL) has changed
2312 if (cursor_info != last_cursor_info2)
2313 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2315 last_cursor_info2 = cursor_info;
2319 // ============================================================================
2321 // ============================================================================
2323 void SDLOpenAudio(void)
2325 if (program.headless)
2328 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2330 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2334 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2335 AUDIO_NUM_CHANNELS_STEREO,
2336 setup.system.audio_fragment_size) < 0)
2338 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2342 audio.sound_available = TRUE;
2343 audio.music_available = TRUE;
2344 audio.loops_available = TRUE;
2345 audio.sound_enabled = TRUE;
2347 // set number of available mixer channels
2348 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2349 audio.music_channel = MUSIC_CHANNEL;
2350 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2352 Mixer_InitChannels();
2355 void SDLCloseAudio(void)
2358 Mix_HaltChannel(-1);
2361 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2365 // ============================================================================
2367 // ============================================================================
2369 void SDLWaitEvent(Event *event)
2371 SDL_WaitEvent(event);
2374 void SDLCorrectRawMousePosition(int *x, int *y)
2376 if (sdl_renderer == NULL)
2379 // this corrects the raw mouse position for logical screen size within event
2380 // filters (correction done later by SDL library when handling mouse events)
2383 float scale_x, scale_y;
2385 SDL_RenderGetViewport(sdl_renderer, &viewport);
2386 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2388 *x = (int)(*x / scale_x);
2389 *y = (int)(*y / scale_y);
2396 // ============================================================================
2397 // joystick functions
2398 // ============================================================================
2400 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2401 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2402 static int sdl_js_axis[MAX_PLAYERS][2];
2403 static int sdl_js_button[MAX_PLAYERS][2];
2404 static boolean sdl_is_controller[MAX_PLAYERS];
2406 void SDLClearJoystickState(void)
2410 for (i = 0; i < MAX_PLAYERS; i++)
2412 for (j = 0; j < 2; j++)
2414 sdl_js_axis_raw[i][j] = -1;
2415 sdl_js_axis[i][j] = 0;
2416 sdl_js_button[i][j] = 0;
2421 boolean SDLOpenJoystick(int nr)
2423 if (nr < 0 || nr >= MAX_PLAYERS)
2426 sdl_is_controller[nr] = SDL_IsGameController(nr);
2429 Error(ERR_DEBUG, "opening joystick %d (%s)",
2430 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2433 if (sdl_is_controller[nr])
2434 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2436 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2438 return (sdl_joystick[nr] != NULL);
2441 void SDLCloseJoystick(int nr)
2443 if (nr < 0 || nr >= MAX_PLAYERS)
2447 Error(ERR_DEBUG, "closing joystick %d", nr);
2450 if (sdl_is_controller[nr])
2451 SDL_GameControllerClose(sdl_joystick[nr]);
2453 SDL_JoystickClose(sdl_joystick[nr]);
2455 sdl_joystick[nr] = NULL;
2458 boolean SDLCheckJoystickOpened(int nr)
2460 if (nr < 0 || nr >= MAX_PLAYERS)
2463 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2466 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2468 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2469 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2470 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2471 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2473 if (nr < 0 || nr >= MAX_PLAYERS)
2479 // prevent (slightly jittering, but centered) axis A from resetting axis B
2480 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2481 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2484 sdl_js_axis[nr][axis_id] = axis_value;
2485 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2488 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2490 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2491 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2492 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2493 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2494 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2495 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2496 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2497 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2500 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2501 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2502 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2503 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2504 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2505 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2506 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2507 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2509 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2510 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2511 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2512 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2513 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2515 if (nr < 0 || nr >= MAX_PLAYERS)
2518 if (button_id == -1)
2521 sdl_js_button[nr][button_id] = button_state;
2524 void HandleJoystickEvent(Event *event)
2528 case SDL_CONTROLLERDEVICEADDED:
2530 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2531 event->cdevice.which);
2536 case SDL_CONTROLLERDEVICEREMOVED:
2538 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2539 event->cdevice.which);
2544 case SDL_CONTROLLERAXISMOTION:
2546 Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2547 event->caxis.which, event->caxis.axis, event->caxis.value);
2549 setJoystickAxis(event->caxis.which,
2551 event->caxis.value);
2554 case SDL_CONTROLLERBUTTONDOWN:
2556 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2557 event->cbutton.which, event->cbutton.button);
2559 setJoystickButton(event->cbutton.which,
2560 event->cbutton.button,
2564 case SDL_CONTROLLERBUTTONUP:
2566 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2567 event->cbutton.which, event->cbutton.button);
2569 setJoystickButton(event->cbutton.which,
2570 event->cbutton.button,
2574 case SDL_JOYAXISMOTION:
2575 if (sdl_is_controller[event->jaxis.which])
2579 Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2580 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2582 if (event->jaxis.axis < 4)
2583 setJoystickAxis(event->jaxis.which,
2585 event->jaxis.value);
2588 case SDL_JOYBUTTONDOWN:
2589 if (sdl_is_controller[event->jaxis.which])
2593 Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2594 event->jbutton.which, event->jbutton.button);
2596 if (event->jbutton.button < 4)
2597 setJoystickButton(event->jbutton.which,
2598 event->jbutton.button,
2602 case SDL_JOYBUTTONUP:
2603 if (sdl_is_controller[event->jaxis.which])
2607 Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2608 event->jbutton.which, event->jbutton.button);
2610 if (event->jbutton.button < 4)
2611 setJoystickButton(event->jbutton.which,
2612 event->jbutton.button,
2621 void SDLInitJoysticks(void)
2623 static boolean sdl_joystick_subsystem_initialized = FALSE;
2624 boolean print_warning = !sdl_joystick_subsystem_initialized;
2625 char *mappings_file_base = getPath2(options.conf_directory,
2626 GAMECONTROLLER_BASENAME);
2627 char *mappings_file_user = getPath2(getUserGameDataDir(),
2628 GAMECONTROLLER_BASENAME);
2632 if (!sdl_joystick_subsystem_initialized)
2634 sdl_joystick_subsystem_initialized = TRUE;
2636 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2638 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2640 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2644 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2646 // the included game controller base mappings should always be found
2647 if (num_mappings == -1)
2648 Error(ERR_WARN, "no game controller base mappings found");
2651 Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2654 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2657 // the personal game controller user mappings may or may not be found
2658 if (num_mappings == -1)
2659 Error(ERR_WARN, "no game controller user mappings found");
2661 Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2663 Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2666 checked_free(mappings_file_base);
2667 checked_free(mappings_file_user);
2670 for (i = 0; i < SDL_NumJoysticks(); i++)
2672 const char *name, *type;
2674 if (SDL_IsGameController(i))
2676 name = SDL_GameControllerNameForIndex(i);
2677 type = "game controller";
2681 name = SDL_JoystickNameForIndex(i);
2685 Error(ERR_INFO, "- joystick %d (%s): '%s'",
2686 i, type, (name ? name : "(Unknown)"));
2691 // assign joysticks from configured to connected joystick for all players
2692 for (i = 0; i < MAX_PLAYERS; i++)
2694 // get configured joystick for this player
2695 char *device_name = setup.input[i].joy.device_name;
2696 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2698 if (joystick_nr >= SDL_NumJoysticks())
2700 if (setup.input[i].use_joystick && print_warning)
2701 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2706 // store configured joystick number for each player
2707 joystick.nr[i] = joystick_nr;
2710 // now open all connected joysticks (regardless if configured or not)
2711 for (i = 0; i < SDL_NumJoysticks(); i++)
2713 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2714 if (SDLCheckJoystickOpened(i))
2715 SDLCloseJoystick(i);
2717 if (SDLOpenJoystick(i))
2718 joystick.status = JOYSTICK_ACTIVATED;
2719 else if (print_warning)
2720 Error(ERR_WARN, "cannot open joystick %d", i);
2723 SDLClearJoystickState();
2726 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2728 if (nr < 0 || nr >= MAX_PLAYERS)
2732 *x = sdl_js_axis[nr][0];
2734 *y = sdl_js_axis[nr][1];
2737 *b1 = sdl_js_button[nr][0];
2739 *b2 = sdl_js_button[nr][1];
2745 // ============================================================================
2746 // touch input overlay functions
2747 // ============================================================================
2749 #if defined(USE_TOUCH_INPUT_OVERLAY)
2750 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2753 int grid_xsize = overlay.grid_xsize;
2754 int grid_ysize = overlay.grid_ysize;
2757 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2758 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2760 for (x = 0; x < grid_xsize; x++)
2762 rect.x = (x + 0) * video.screen_width / grid_xsize;
2763 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2765 for (y = 0; y < grid_ysize; y++)
2767 rect.y = (y + 0) * video.screen_height / grid_ysize;
2768 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2770 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2771 SDL_RenderDrawRect(sdl_renderer, &rect);
2775 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2778 static void RenderFillRectangle(int x, int y, int width, int height)
2780 SDL_Rect rect = { x, y, width, height };
2782 SDL_RenderFillRect(sdl_renderer, &rect);
2785 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2787 static int alpha_direction = 0;
2788 static int alpha_highlight = 0;
2789 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2790 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2792 int grid_xsize = overlay.grid_xsize;
2793 int grid_ysize = overlay.grid_ysize;
2796 if (alpha == alpha_max)
2798 if (alpha_direction < 0)
2800 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2802 if (alpha_highlight == 0)
2803 alpha_direction = 1;
2807 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2809 if (alpha_highlight == alpha_max)
2810 alpha_direction = -1;
2815 alpha_direction = 1;
2816 alpha_highlight = alpha;
2819 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2821 for (x = 0; x < grid_xsize; x++)
2823 for (y = 0; y < grid_ysize; y++)
2825 int grid_button = overlay.grid_button[x][y];
2826 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2827 int alpha_draw = alpha;
2828 int outline_border = MV_NONE;
2829 int border_size = 2;
2830 boolean draw_outlined = setup.touch.draw_outlined;
2831 boolean draw_pressed = setup.touch.draw_pressed;
2833 if (grid_button == CHAR_GRID_BUTTON_NONE)
2836 if (grid_button == overlay.grid_button_highlight)
2838 draw_outlined = FALSE;
2839 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2842 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2845 draw_outlined = FALSE;
2847 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2850 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2852 rect.x = (x + 0) * video.screen_width / grid_xsize;
2853 rect.y = (y + 0) * video.screen_height / grid_ysize;
2854 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2855 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2857 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2859 rect.x += border_size;
2860 rect.w -= border_size;
2862 outline_border |= MV_LEFT;
2865 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2867 rect.w -= border_size;
2869 outline_border |= MV_RIGHT;
2872 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2874 rect.y += border_size;
2875 rect.h -= border_size;
2877 outline_border |= MV_UP;
2880 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2882 rect.h -= border_size;
2884 outline_border |= MV_DOWN;
2889 int rect_x = rect.x +
2890 (outline_border & MV_LEFT ? border_size : 0);
2891 int rect_w = rect.w -
2892 (outline_border & MV_LEFT ? border_size : 0) -
2893 (outline_border & MV_RIGHT ? border_size : 0);
2895 if (outline_border & MV_LEFT)
2896 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2898 if (outline_border & MV_RIGHT)
2899 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2900 border_size, rect.h);
2902 if (outline_border & MV_UP)
2903 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2905 if (outline_border & MV_DOWN)
2906 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
2907 rect_w, border_size);
2911 SDL_RenderFillRect(sdl_renderer, &rect);
2916 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2919 static void DrawTouchInputOverlay(void)
2921 static boolean deactivated = TRUE;
2922 static boolean show_grid = FALSE;
2923 static int alpha = 0;
2924 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2925 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2926 boolean active = (overlay.enabled && overlay.active);
2928 if (!active && deactivated)
2933 if (alpha < alpha_max)
2934 alpha = MIN(alpha + alpha_step, alpha_max);
2936 deactivated = FALSE;
2940 alpha = MAX(0, alpha - alpha_step);
2946 if (overlay.show_grid)
2948 else if (deactivated)
2952 DrawTouchInputOverlay_ShowGrid(alpha);
2954 DrawTouchInputOverlay_ShowGridButtons(alpha);
2957 static void DrawTouchGadgetsOverlay(void)
2959 DrawGadgets_OverlayTouchButtons();