1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
19 #define ENABLE_UNUSED_CODE 0 // currently unused functions
21 #define DEBUG_JOYSTICKS 0
24 // ============================================================================
26 // ============================================================================
28 // SDL internal variables
29 static SDL_Window *sdl_window = NULL;
30 static SDL_Renderer *sdl_renderer = NULL;
31 static SDL_Texture *sdl_texture_stream = NULL;
32 static SDL_Texture *sdl_texture_target = NULL;
33 static boolean fullscreen_enabled = FALSE;
34 static boolean limit_screen_updates = FALSE;
37 // functions from SGE library
38 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
40 #if defined(USE_TOUCH_INPUT_OVERLAY)
41 // functions to draw overlay graphics for touch device input
42 static void DrawTouchInputOverlay(void);
43 static void DrawTouchGadgetsOverlay(void);
46 void SDLLimitScreenUpdates(boolean enable)
48 limit_screen_updates = enable;
51 static void FinalizeScreen(int draw_target)
53 // copy global animations to render target buffer, if defined (below border)
54 if (gfx.draw_global_anim_function != NULL)
55 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
57 // copy global masked border to render target buffer, if defined
58 if (gfx.draw_global_border_function != NULL)
59 gfx.draw_global_border_function(draw_target);
61 // copy global animations to render target buffer, if defined (above border)
62 if (gfx.draw_global_anim_function != NULL)
63 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
65 // copy tile selection cursor to render target buffer, if defined (above all)
66 if (gfx.draw_tile_cursor_function != NULL)
67 gfx.draw_tile_cursor_function(draw_target);
70 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
72 static unsigned int update_screen_delay = 0;
73 unsigned int update_screen_delay_value = 50; // (milliseconds)
74 SDL_Surface *screen = backbuffer->surface;
76 if (limit_screen_updates &&
77 !DelayReached(&update_screen_delay, update_screen_delay_value))
80 LimitScreenUpdates(FALSE);
84 static int LastFrameCounter = 0;
85 boolean changed = (FrameCounter != LastFrameCounter);
87 printf("::: FrameCounter == %d [%s]\n", FrameCounter,
88 (changed ? "-" : "SAME FRAME UPDATED"));
90 LastFrameCounter = FrameCounter;
99 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
100 gfx.final_screen_bitmap != NULL) // may not be initialized yet
102 // draw global animations using bitmaps instead of using textures
103 // to prevent texture scaling artefacts (this is potentially slower)
105 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
106 gfx.win_xsize, gfx.win_ysize, 0, 0);
108 FinalizeScreen(DRAW_TO_SCREEN);
110 screen = gfx.final_screen_bitmap->surface;
112 // force full window redraw
116 SDL_Texture *sdl_texture = sdl_texture_stream;
118 // deactivate use of target texture if render targets are not supported
119 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
120 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
121 sdl_texture_target == NULL)
122 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
124 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
125 sdl_texture = sdl_texture_target;
129 int bytes_x = screen->pitch / video.width;
130 int bytes_y = screen->pitch;
132 SDL_UpdateTexture(sdl_texture, rect,
133 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
138 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
141 int xoff = video.screen_xoffset;
142 int yoff = video.screen_yoffset;
143 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
144 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
145 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
147 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
148 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
149 dst_rect2 = &dst_rect_screen;
151 dst_rect1 = &dst_rect_screen;
153 #if defined(HAS_SCREEN_KEYBOARD)
154 if (video.shifted_up || video.shifted_up_delay)
156 int time_current = SDL_GetTicks();
157 int pos = video.shifted_up_pos;
158 int pos_last = video.shifted_up_pos_last;
160 if (!DelayReachedExt(&video.shifted_up_delay, video.shifted_up_delay_value,
163 int delay = time_current - video.shifted_up_delay;
164 int delay_value = video.shifted_up_delay_value;
166 pos = pos_last + (pos - pos_last) * delay / delay_value;
170 video.shifted_up_pos_last = pos;
171 video.shifted_up_delay = 0;
174 SDL_Rect src_rect_up = { 0, pos, video.width, video.height - pos };
175 SDL_Rect dst_rect_up = { xoff, yoff, video.width, video.height - pos };
177 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
178 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
180 src_rect2 = &src_rect_up;
181 dst_rect2 = &dst_rect_up;
185 src_rect1 = &src_rect_up;
186 dst_rect1 = &dst_rect_up;
191 // clear render target buffer
192 SDL_RenderClear(sdl_renderer);
194 // set renderer to use target texture for rendering
195 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
196 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
197 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
199 // copy backbuffer texture to render target buffer
200 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
201 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
203 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
204 FinalizeScreen(DRAW_TO_SCREEN);
206 // when using target texture, copy it to screen buffer
207 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
208 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
210 SDL_SetRenderTarget(sdl_renderer, NULL);
211 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
214 #if defined(USE_TOUCH_INPUT_OVERLAY)
215 // draw overlay graphics for touch device input, if needed
216 DrawTouchInputOverlay();
218 // draw overlay gadgets for touch device input, if needed
219 DrawTouchGadgetsOverlay();
222 // global synchronization point of the game to align video frame delay
223 if (with_frame_delay)
224 WaitUntilDelayReached(&video.frame_delay, video.frame_delay_value);
226 video.frame_counter++;
228 // show render target buffer on screen
229 SDL_RenderPresent(sdl_renderer);
232 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
234 PumpEvents(); // execute event filter actions while waiting
236 UpdateScreenExt(rect, TRUE);
239 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
241 UpdateScreenExt(rect, FALSE);
244 void Delay_WithScreenUpdates(unsigned int delay)
246 unsigned int time_current = SDL_GetTicks();
247 unsigned int time_delayed = time_current + delay;
249 while (time_current < time_delayed)
251 // updating the screen contains waiting for frame delay (non-busy)
252 UpdateScreen_WithFrameDelay(NULL);
254 time_current = SDL_GetTicks();
258 static void SDLSetWindowIcon(char *basename)
260 // (setting the window icon on Mac OS X would replace the high-quality
261 // dock icon with the currently smaller (and uglier) icon from file)
263 #if !defined(PLATFORM_MACOSX)
264 char *filename = getCustomImageFilename(basename);
265 SDL_Surface *surface;
267 if (filename == NULL)
269 Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
274 if ((surface = IMG_Load(filename)) == NULL)
276 Error(ERR_WARN, "IMG_Load('%s') failed: %s", basename, SDL_GetError());
281 // set transparent color
282 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
283 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
285 SDL_SetWindowIcon(sdl_window, surface);
289 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
290 SDL_PixelFormat *format2)
292 return (format1->format == format2->format &&
293 format1->BitsPerPixel == format2->BitsPerPixel &&
294 format1->BytesPerPixel == format2->BytesPerPixel &&
295 format1->Rmask == format2->Rmask &&
296 format1->Gmask == format2->Gmask &&
297 format1->Bmask == format2->Bmask);
300 static Pixel SDLGetColorKey(SDL_Surface *surface)
304 if (SDL_GetColorKey(surface, &color_key) != 0)
310 static boolean SDLHasColorKey(SDL_Surface *surface)
312 return (SDLGetColorKey(surface) != -1);
315 static boolean SDLHasAlpha(SDL_Surface *surface)
317 SDL_BlendMode blend_mode;
319 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
322 return (blend_mode == SDL_BLENDMODE_BLEND);
325 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
327 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
329 SDL_SetSurfaceBlendMode(surface, blend_mode);
330 SDL_SetSurfaceAlphaMod(surface, alpha);
333 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
335 SDL_PixelFormat format;
336 SDL_Surface *new_surface;
341 if (backbuffer && backbuffer->surface)
343 format = *backbuffer->surface->format;
344 format.Amask = surface->format->Amask; // keep alpha channel
348 format = *surface->format;
351 new_surface = SDL_ConvertSurface(surface, &format, 0);
353 if (new_surface == NULL)
354 Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
356 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
357 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
358 SDL_SetColorKey(new_surface, SET_TRANSPARENT_PIXEL,
359 SDLGetColorKey(surface));
364 boolean SDLSetNativeSurface(SDL_Surface **surface)
366 SDL_Surface *new_surface;
368 if (surface == NULL ||
370 backbuffer == NULL ||
371 backbuffer->surface == NULL)
374 // if pixel format already optimized for destination surface, do nothing
375 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
378 new_surface = SDLGetNativeSurface(*surface);
380 SDL_FreeSurface(*surface);
382 *surface = new_surface;
387 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
389 if (program.headless)
392 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
395 Error(ERR_EXIT, "SDL_CreateTextureFromSurface() failed: %s",
401 void SDLCreateBitmapTextures(Bitmap *bitmap)
407 SDL_DestroyTexture(bitmap->texture);
408 if (bitmap->texture_masked)
409 SDL_DestroyTexture(bitmap->texture_masked);
411 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
412 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
415 void SDLFreeBitmapTextures(Bitmap *bitmap)
421 SDL_DestroyTexture(bitmap->texture);
422 if (bitmap->texture_masked)
423 SDL_DestroyTexture(bitmap->texture_masked);
425 bitmap->texture = NULL;
426 bitmap->texture_masked = NULL;
429 void SDLInitVideoDisplay(void)
431 // initialize SDL video
432 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
433 Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
435 // set default SDL depth
436 video.default_depth = 32; // (how to determine video depth in SDL2?)
438 // Code used with SDL 1.2:
439 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
442 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
444 if (program.headless)
447 video.window_scaling_percent = setup.window_scaling_percent;
448 video.window_scaling_quality = setup.window_scaling_quality;
450 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
452 // SDL 2.0: support for (desktop) fullscreen mode available
453 video.fullscreen_available = TRUE;
455 // open SDL video output device (window or fullscreen mode)
456 if (!SDLSetVideoMode(fullscreen))
457 Error(ERR_EXIT, "setting video mode failed");
459 // !!! SDL2 can only set the window icon if the window already exists !!!
461 SDLSetWindowIcon(program.icon_filename);
463 // set window and icon title
467 static void SDLInitVideoBuffer_DrawBuffer(void)
469 /* SDL cannot directly draw to the visible video framebuffer like X11,
470 but always uses a backbuffer, which is then blitted to the visible
471 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
472 visible video framebuffer with 'SDL_Flip', if the hardware supports
473 this). Therefore do not use an additional backbuffer for drawing, but
474 use a symbolic buffer (distinguishable from the SDL backbuffer) called
475 'window', which indicates that the SDL backbuffer should be updated to
476 the visible video framebuffer when attempting to blit to it.
478 For convenience, it seems to be a good idea to create this symbolic
479 buffer 'window' at the same size as the SDL backbuffer. Although it
480 should never be drawn to directly, it would do no harm nevertheless. */
482 // create additional (symbolic) buffer for double-buffering
483 ReCreateBitmap(&window, video.width, video.height);
485 // create dummy drawing buffer for headless mode, if needed
486 if (program.headless)
487 ReCreateBitmap(&backbuffer, video.width, video.height);
490 void SDLInitVideoBuffer(boolean fullscreen)
492 SDLInitVideoBuffer_VideoBuffer(fullscreen);
493 SDLInitVideoBuffer_DrawBuffer();
496 static boolean SDLCreateScreen(boolean fullscreen)
498 SDL_Surface *new_surface = NULL;
500 int surface_flags_window = SURFACE_FLAGS;
501 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
504 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
506 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
507 _without_ enabling 2D/3D acceleration and/or guest additions installed,
508 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
509 it will try to use accelerated graphics and apparently fails miserably) */
510 int renderer_flags = SDL_RENDERER_SOFTWARE;
513 int width = video.width;
514 int height = video.height;
515 int screen_width = video.screen_width;
516 int screen_height = video.screen_height;
517 int surface_flags = (fullscreen ? surface_flags_fullscreen :
518 surface_flags_window);
520 // default window size is unscaled
521 video.window_width = screen_width;
522 video.window_height = screen_height;
524 // store if initial screen mode is fullscreen mode when changing screen size
525 video.fullscreen_initial = fullscreen;
527 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
529 video.window_width = window_scaling_factor * screen_width;
530 video.window_height = window_scaling_factor * screen_height;
532 if (sdl_texture_stream)
534 SDL_DestroyTexture(sdl_texture_stream);
535 sdl_texture_stream = NULL;
538 if (sdl_texture_target)
540 SDL_DestroyTexture(sdl_texture_target);
541 sdl_texture_target = NULL;
544 if (!(fullscreen && fullscreen_enabled))
548 SDL_DestroyRenderer(sdl_renderer);
554 SDL_DestroyWindow(sdl_window);
559 if (sdl_window == NULL)
560 sdl_window = SDL_CreateWindow(program.window_title,
561 SDL_WINDOWPOS_CENTERED,
562 SDL_WINDOWPOS_CENTERED,
567 if (sdl_window != NULL)
569 if (sdl_renderer == NULL)
570 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
572 if (sdl_renderer != NULL)
574 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
575 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
577 SDLSetScreenVsyncMode(setup.vsync_mode);
579 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
580 SDL_PIXELFORMAT_ARGB8888,
581 SDL_TEXTUREACCESS_STREAMING,
584 if (SDL_RenderTargetSupported(sdl_renderer))
585 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
586 SDL_PIXELFORMAT_ARGB8888,
587 SDL_TEXTUREACCESS_TARGET,
590 if (sdl_texture_stream != NULL)
592 // use SDL default values for RGB masks and no alpha channel
593 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
595 if (new_surface == NULL)
596 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
600 Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
605 Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
610 Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
613 SDLSetScreenProperties();
615 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
616 if (new_surface != NULL)
617 fullscreen_enabled = fullscreen;
619 if (backbuffer == NULL)
620 backbuffer = CreateBitmapStruct();
622 backbuffer->width = video.width;
623 backbuffer->height = video.height;
625 if (backbuffer->surface)
626 SDL_FreeSurface(backbuffer->surface);
628 backbuffer->surface = new_surface;
630 return (new_surface != NULL);
633 boolean SDLSetVideoMode(boolean fullscreen)
635 boolean success = FALSE;
639 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
641 // switch display to fullscreen mode, if available
642 success = SDLCreateScreen(TRUE);
646 // switching display to fullscreen mode failed -- do not try it again
647 video.fullscreen_available = FALSE;
651 video.fullscreen_enabled = TRUE;
655 if ((!fullscreen && video.fullscreen_enabled) || !success)
657 // switch display to window mode
658 success = SDLCreateScreen(FALSE);
662 // switching display to window mode failed -- should not happen
666 video.fullscreen_enabled = FALSE;
667 video.window_scaling_percent = setup.window_scaling_percent;
668 video.window_scaling_quality = setup.window_scaling_quality;
670 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
674 SDLRedrawWindow(); // map window
679 void SDLSetWindowTitle(void)
681 if (sdl_window == NULL)
684 SDL_SetWindowTitle(sdl_window, program.window_title);
687 void SDLSetWindowScaling(int window_scaling_percent)
689 if (sdl_window == NULL)
692 float window_scaling_factor = (float)window_scaling_percent / 100;
693 int new_window_width = (int)(window_scaling_factor * video.screen_width);
694 int new_window_height = (int)(window_scaling_factor * video.screen_height);
696 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
698 video.window_scaling_percent = window_scaling_percent;
699 video.window_width = new_window_width;
700 video.window_height = new_window_height;
705 void SDLSetWindowScalingQuality(char *window_scaling_quality)
707 SDL_Texture *new_texture;
709 if (sdl_texture_stream == NULL)
712 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
714 new_texture = SDL_CreateTexture(sdl_renderer,
715 SDL_PIXELFORMAT_ARGB8888,
716 SDL_TEXTUREACCESS_STREAMING,
717 video.width, video.height);
719 if (new_texture != NULL)
721 SDL_DestroyTexture(sdl_texture_stream);
723 sdl_texture_stream = new_texture;
726 if (SDL_RenderTargetSupported(sdl_renderer))
727 new_texture = SDL_CreateTexture(sdl_renderer,
728 SDL_PIXELFORMAT_ARGB8888,
729 SDL_TEXTUREACCESS_TARGET,
730 video.width, video.height);
734 if (new_texture != NULL)
736 SDL_DestroyTexture(sdl_texture_target);
738 sdl_texture_target = new_texture;
743 video.window_scaling_quality = window_scaling_quality;
746 void SDLSetWindowFullscreen(boolean fullscreen)
748 if (sdl_window == NULL)
751 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
753 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
754 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
756 // if screen size was changed in fullscreen mode, correct desktop window size
757 if (!fullscreen && video.fullscreen_initial)
759 SDLSetWindowScaling(setup.window_scaling_percent);
760 SDL_SetWindowPosition(sdl_window,
761 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
763 video.fullscreen_initial = FALSE;
767 void SDLSetDisplaySize(void)
769 if (sdl_renderer != NULL)
773 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
775 video.display_width = w;
776 video.display_height = h;
779 Error(ERR_DEBUG, "SDL renderer size: %d x %d",
780 video.display_width, video.display_height);
785 SDL_Rect display_bounds;
787 SDL_GetDisplayBounds(0, &display_bounds);
789 video.display_width = display_bounds.w;
790 video.display_height = display_bounds.h;
793 Error(ERR_DEBUG, "SDL display size: %d x %d",
794 video.display_width, video.display_height);
799 void SDLSetScreenSizeAndOffsets(int width, int height)
801 // set default video screen size and offsets
802 video.screen_width = width;
803 video.screen_height = height;
804 video.screen_xoffset = 0;
805 video.screen_yoffset = 0;
807 #if defined(USE_COMPLETE_DISPLAY)
808 float ratio_video = (float) width / height;
809 float ratio_display = (float) video.display_width / video.display_height;
811 if (ratio_video != ratio_display)
813 // adjust drawable screen size to cover the whole device display
815 if (ratio_video < ratio_display)
816 video.screen_width *= ratio_display / ratio_video;
818 video.screen_height *= ratio_video / ratio_display;
820 video.screen_xoffset = (video.screen_width - width) / 2;
821 video.screen_yoffset = (video.screen_height - height) / 2;
824 Error(ERR_DEBUG, "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
826 video.screen_width, video.screen_height,
827 ratio_video, ratio_display);
833 void SDLSetScreenSizeForRenderer(int width, int height)
835 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
838 void SDLSetScreenProperties(void)
841 SDLSetScreenSizeAndOffsets(video.width, video.height);
842 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
845 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
847 video.screen_rendering_mode =
848 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
849 SPECIAL_RENDERING_BITMAP :
850 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
851 SPECIAL_RENDERING_TARGET:
852 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
853 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
856 void SDLSetScreenVsyncMode(char *vsync_mode)
859 (strEqual(vsync_mode, STR_VSYNC_MODE_NORMAL) ? VSYNC_MODE_NORMAL :
860 strEqual(vsync_mode, STR_VSYNC_MODE_ADAPTIVE) ? VSYNC_MODE_ADAPTIVE :
862 int result = SDL_GL_SetSwapInterval(interval);
864 // if adaptive vsync requested, but not supported, retry with normal vsync
865 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
866 SDL_GL_SetSwapInterval(VSYNC_MODE_NORMAL);
869 void SDLRedrawWindow(void)
871 UpdateScreen_WithoutFrameDelay(NULL);
874 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
877 if (program.headless)
880 SDL_Surface *surface =
881 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
884 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
886 SDLSetNativeSurface(&surface);
888 bitmap->surface = surface;
891 void SDLFreeBitmapPointers(Bitmap *bitmap)
894 SDL_FreeSurface(bitmap->surface);
895 if (bitmap->surface_masked)
896 SDL_FreeSurface(bitmap->surface_masked);
898 bitmap->surface = NULL;
899 bitmap->surface_masked = NULL;
902 SDL_DestroyTexture(bitmap->texture);
903 if (bitmap->texture_masked)
904 SDL_DestroyTexture(bitmap->texture_masked);
906 bitmap->texture = NULL;
907 bitmap->texture_masked = NULL;
910 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
911 int src_x, int src_y, int width, int height,
912 int dst_x, int dst_y, int mask_mode)
914 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
915 SDL_Rect src_rect, dst_rect;
927 // if (src_bitmap != backbuffer || dst_bitmap != window)
928 if (!(src_bitmap == backbuffer && dst_bitmap == window))
929 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
930 src_bitmap->surface_masked : src_bitmap->surface),
931 &src_rect, real_dst_bitmap->surface, &dst_rect);
933 if (dst_bitmap == window)
934 UpdateScreen_WithFrameDelay(&dst_rect);
937 void SDLBlitTexture(Bitmap *bitmap,
938 int src_x, int src_y, int width, int height,
939 int dst_x, int dst_y, int mask_mode)
941 SDL_Texture *texture;
946 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
961 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
964 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
967 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
975 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
977 if (dst_bitmap == window)
978 UpdateScreen_WithFrameDelay(&rect);
981 void PrepareFadeBitmap(int draw_target)
983 Bitmap *fade_bitmap =
984 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
985 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
987 if (fade_bitmap == NULL)
990 // copy backbuffer to fading buffer
991 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
993 // add border and animations to fading buffer
994 FinalizeScreen(draw_target);
997 void SDLFadeRectangle(int x, int y, int width, int height,
998 int fade_mode, int fade_delay, int post_delay,
999 void (*draw_border_function)(void))
1001 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1002 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1003 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1004 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1005 SDL_Surface *surface_screen = backbuffer->surface;
1006 SDL_Rect src_rect, dst_rect;
1008 int src_x = x, src_y = y;
1009 int dst_x = x, dst_y = y;
1010 unsigned int time_last, time_current;
1012 // store function for drawing global masked border
1013 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1015 // deactivate drawing of global border while fading, if needed
1016 if (draw_border_function == NULL)
1017 gfx.draw_global_border_function = NULL;
1022 src_rect.h = height;
1026 dst_rect.w = width; // (ignored)
1027 dst_rect.h = height; // (ignored)
1029 dst_rect2 = dst_rect;
1031 // before fading in, store backbuffer (without animation graphics)
1032 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1033 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1035 // copy source and target surfaces to temporary surfaces for fading
1036 if (fade_mode & FADE_TYPE_TRANSFORM)
1038 // (source and target fading buffer already prepared)
1040 else if (fade_mode & FADE_TYPE_FADE_IN)
1042 // (target fading buffer already prepared)
1043 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1045 else // FADE_TYPE_FADE_OUT
1047 // (source fading buffer already prepared)
1048 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1051 time_current = SDL_GetTicks();
1053 if (fade_delay <= 0)
1055 // immediately draw final target frame without delay
1056 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1060 // when fading without delay, also skip post delay
1064 if (fade_mode == FADE_MODE_MELT)
1066 boolean done = FALSE;
1067 int melt_pixels = 2;
1068 int melt_columns = width / melt_pixels;
1069 int ypos[melt_columns];
1070 int max_steps = height / 8 + 32;
1075 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1077 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1079 ypos[0] = -GetSimpleRandom(16);
1081 for (i = 1 ; i < melt_columns; i++)
1083 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1085 ypos[i] = ypos[i - 1] + r;
1098 time_last = time_current;
1099 time_current = SDL_GetTicks();
1100 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1101 steps_final = MIN(MAX(0, steps), max_steps);
1105 done = (steps_done >= steps_final);
1107 for (i = 0 ; i < melt_columns; i++)
1115 else if (ypos[i] < height)
1120 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1122 if (ypos[i] + dy >= height)
1123 dy = height - ypos[i];
1125 // copy part of (appearing) target surface to upper area
1126 src_rect.x = src_x + i * melt_pixels;
1127 // src_rect.y = src_y + ypos[i];
1129 src_rect.w = melt_pixels;
1131 src_rect.h = ypos[i] + dy;
1133 dst_rect.x = dst_x + i * melt_pixels;
1134 // dst_rect.y = dst_y + ypos[i];
1137 if (steps_done >= steps_final)
1138 SDL_BlitSurface(surface_target, &src_rect,
1139 surface_screen, &dst_rect);
1143 // copy part of (disappearing) source surface to lower area
1144 src_rect.x = src_x + i * melt_pixels;
1146 src_rect.w = melt_pixels;
1147 src_rect.h = height - ypos[i];
1149 dst_rect.x = dst_x + i * melt_pixels;
1150 dst_rect.y = dst_y + ypos[i];
1152 if (steps_done >= steps_final)
1153 SDL_BlitSurface(surface_source, &src_rect,
1154 surface_screen, &dst_rect);
1160 src_rect.x = src_x + i * melt_pixels;
1162 src_rect.w = melt_pixels;
1163 src_rect.h = height;
1165 dst_rect.x = dst_x + i * melt_pixels;
1168 if (steps_done >= steps_final)
1169 SDL_BlitSurface(surface_target, &src_rect,
1170 surface_screen, &dst_rect);
1174 if (steps_done >= steps_final)
1176 if (draw_border_function != NULL)
1177 draw_border_function();
1179 UpdateScreen_WithFrameDelay(&dst_rect2);
1183 else if (fade_mode == FADE_MODE_CURTAIN)
1187 int xx_size = width / 2;
1189 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1191 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1193 for (xx = 0; xx < xx_size;)
1195 time_last = time_current;
1196 time_current = SDL_GetTicks();
1197 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1198 xx_final = MIN(MAX(0, xx), xx_size);
1203 src_rect.h = height;
1208 // draw new (target) image to screen buffer
1209 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1211 if (xx_final < xx_size)
1213 src_rect.w = xx_size - xx_final;
1214 src_rect.h = height;
1216 // draw old (source) image to screen buffer (left side)
1218 src_rect.x = src_x + xx_final;
1221 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1223 // draw old (source) image to screen buffer (right side)
1225 src_rect.x = src_x + xx_size;
1226 dst_rect.x = dst_x + xx_size + xx_final;
1228 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1231 if (draw_border_function != NULL)
1232 draw_border_function();
1234 // only update the region of the screen that is affected from fading
1235 UpdateScreen_WithFrameDelay(&dst_rect2);
1238 else // fading in, fading out or cross-fading
1243 for (alpha = 0.0; alpha < 255.0;)
1245 time_last = time_current;
1246 time_current = SDL_GetTicks();
1247 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1248 alpha_final = MIN(MAX(0, alpha), 255);
1250 // draw existing (source) image to screen buffer
1251 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1253 // draw new (target) image to screen buffer using alpha blending
1254 SDLSetAlpha(surface_target, TRUE, alpha_final);
1255 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1257 if (draw_border_function != NULL)
1258 draw_border_function();
1260 // only update the region of the screen that is affected from fading
1261 UpdateScreen_WithFrameDelay(&dst_rect);
1266 Delay_WithScreenUpdates(post_delay);
1268 // restore function for drawing global masked border
1269 gfx.draw_global_border_function = draw_global_border_function;
1271 // after fading in, restore backbuffer (without animation graphics)
1272 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1273 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1276 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1277 int to_x, int to_y, Uint32 color)
1279 SDL_Surface *surface = dst_bitmap->surface;
1283 swap_numbers(&from_x, &to_x);
1286 swap_numbers(&from_y, &to_y);
1290 rect.w = (to_x - from_x + 1);
1291 rect.h = (to_y - from_y + 1);
1293 SDL_FillRect(surface, &rect, color);
1296 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1297 int to_x, int to_y, Uint32 color)
1299 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1302 #if ENABLE_UNUSED_CODE
1303 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1304 int num_points, Uint32 color)
1309 for (i = 0; i < num_points - 1; i++)
1311 for (x = 0; x < line_width; x++)
1313 for (y = 0; y < line_width; y++)
1315 int dx = x - line_width / 2;
1316 int dy = y - line_width / 2;
1318 if ((x == 0 && y == 0) ||
1319 (x == 0 && y == line_width - 1) ||
1320 (x == line_width - 1 && y == 0) ||
1321 (x == line_width - 1 && y == line_width - 1))
1324 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1325 points[i+1].x + dx, points[i+1].y + dy, color);
1332 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1334 SDL_Surface *surface = src_bitmap->surface;
1336 switch (surface->format->BytesPerPixel)
1338 case 1: // assuming 8-bpp
1340 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1344 case 2: // probably 15-bpp or 16-bpp
1346 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1350 case 3: // slow 24-bpp mode; usually not used
1353 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1357 shift = surface->format->Rshift;
1358 color |= *(pix + shift / 8) >> shift;
1359 shift = surface->format->Gshift;
1360 color |= *(pix + shift / 8) >> shift;
1361 shift = surface->format->Bshift;
1362 color |= *(pix + shift / 8) >> shift;
1368 case 4: // probably 32-bpp
1370 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1379 // ============================================================================
1380 // The following functions were taken from the SGE library
1381 // (SDL Graphics Extension Library) by Anders Lindström
1382 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1383 // ============================================================================
1385 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1387 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1389 switch (surface->format->BytesPerPixel)
1394 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1400 // Probably 15-bpp or 16-bpp
1401 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1407 // Slow 24-bpp mode, usually not used
1411 // Gack - slow, but endian correct
1412 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1413 shift = surface->format->Rshift;
1414 *(pix+shift/8) = color>>shift;
1415 shift = surface->format->Gshift;
1416 *(pix+shift/8) = color>>shift;
1417 shift = surface->format->Bshift;
1418 *(pix+shift/8) = color>>shift;
1425 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1433 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1434 Uint8 R, Uint8 G, Uint8 B)
1436 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1439 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1441 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1444 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1446 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1449 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1454 // Gack - slow, but endian correct
1455 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1456 shift = surface->format->Rshift;
1457 *(pix+shift/8) = color>>shift;
1458 shift = surface->format->Gshift;
1459 *(pix+shift/8) = color>>shift;
1460 shift = surface->format->Bshift;
1461 *(pix+shift/8) = color>>shift;
1464 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1466 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1469 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1471 switch (dest->format->BytesPerPixel)
1474 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1478 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1482 _PutPixel24(dest,x,y,color);
1486 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1492 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1494 if (SDL_MUSTLOCK(surface))
1496 if (SDL_LockSurface(surface) < 0)
1502 _PutPixel(surface, x, y, color);
1504 if (SDL_MUSTLOCK(surface))
1506 SDL_UnlockSurface(surface);
1511 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1512 Uint8 r, Uint8 g, Uint8 b)
1514 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1517 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1519 if (y >= 0 && y <= dest->h - 1)
1521 switch (dest->format->BytesPerPixel)
1524 return y*dest->pitch;
1528 return y*dest->pitch/2;
1532 return y*dest->pitch;
1536 return y*dest->pitch/4;
1544 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1547 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1549 switch (surface->format->BytesPerPixel)
1554 *((Uint8 *)surface->pixels + ypitch + x) = color;
1560 // Probably 15-bpp or 16-bpp
1561 *((Uint16 *)surface->pixels + ypitch + x) = color;
1567 // Slow 24-bpp mode, usually not used
1571 // Gack - slow, but endian correct
1572 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1573 shift = surface->format->Rshift;
1574 *(pix+shift/8) = color>>shift;
1575 shift = surface->format->Gshift;
1576 *(pix+shift/8) = color>>shift;
1577 shift = surface->format->Bshift;
1578 *(pix+shift/8) = color>>shift;
1585 *((Uint32 *)surface->pixels + ypitch + x) = color;
1592 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1597 if (SDL_MUSTLOCK(Surface))
1599 if (SDL_LockSurface(Surface) < 0)
1613 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1617 if (x2 > Surface->w - 1)
1618 x2 = Surface->w - 1;
1625 SDL_FillRect(Surface, &l, Color);
1627 if (SDL_MUSTLOCK(Surface))
1629 SDL_UnlockSurface(Surface);
1633 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1634 Uint8 R, Uint8 G, Uint8 B)
1636 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1639 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1652 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1656 if (x2 > Surface->w - 1)
1657 x2 = Surface->w - 1;
1664 SDL_FillRect(Surface, &l, Color);
1667 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1672 if (SDL_MUSTLOCK(Surface))
1674 if (SDL_LockSurface(Surface) < 0)
1688 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1692 if (y2 > Surface->h - 1)
1693 y2 = Surface->h - 1;
1700 SDL_FillRect(Surface, &l, Color);
1702 if (SDL_MUSTLOCK(Surface))
1704 SDL_UnlockSurface(Surface);
1708 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1709 Uint8 R, Uint8 G, Uint8 B)
1711 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1714 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1727 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1731 if (y2 > Surface->h - 1)
1732 y2 = Surface->h - 1;
1739 SDL_FillRect(Surface, &l, Color);
1743 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1744 Sint16 x2, Sint16 y2, Uint32 Color,
1745 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1748 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1753 sdx = (dx < 0) ? -1 : 1;
1754 sdy = (dy < 0) ? -1 : 1;
1766 for (x = 0; x < dx; x++)
1768 Callback(Surface, px, py, Color);
1782 for (y = 0; y < dy; y++)
1784 Callback(Surface, px, py, Color);
1799 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1800 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1801 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1804 sge_DoLine(Surface, X1, Y1, X2, Y2,
1805 SDL_MapRGB(Surface->format, R, G, B), Callback);
1809 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1812 if (SDL_MUSTLOCK(Surface))
1814 if (SDL_LockSurface(Surface) < 0)
1819 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1821 // unlock the display
1822 if (SDL_MUSTLOCK(Surface))
1824 SDL_UnlockSurface(Surface);
1829 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1830 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1832 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1836 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1838 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1842 // ----------------------------------------------------------------------------
1843 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1844 // ----------------------------------------------------------------------------
1846 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1847 int width, int height, Uint32 color)
1851 for (y = src_y; y < src_y + height; y++)
1853 for (x = src_x; x < src_x + width; x++)
1855 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1857 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1862 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1863 int src_x, int src_y, int width, int height,
1864 int dst_x, int dst_y)
1868 for (y = 0; y < height; y++)
1870 for (x = 0; x < width; x++)
1872 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1874 if (pixel != BLACK_PIXEL)
1875 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1881 // ============================================================================
1882 // The following functions were taken from the SDL_gfx library version 2.0.3
1883 // (Rotozoomer) by Andreas Schiffler
1884 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1885 // ============================================================================
1887 // ----------------------------------------------------------------------------
1890 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1891 // ----------------------------------------------------------------------------
1901 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1904 tColorRGBA *sp, *csp, *dp;
1908 sp = csp = (tColorRGBA *) src->pixels;
1909 dp = (tColorRGBA *) dst->pixels;
1910 dgap = dst->pitch - dst->w * 4;
1912 for (y = 0; y < dst->h; y++)
1916 for (x = 0; x < dst->w; x++)
1918 tColorRGBA *sp0 = sp;
1919 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1920 tColorRGBA *sp00 = &sp0[0];
1921 tColorRGBA *sp01 = &sp0[1];
1922 tColorRGBA *sp10 = &sp1[0];
1923 tColorRGBA *sp11 = &sp1[1];
1926 // create new color pixel from all four source color pixels
1927 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1928 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1929 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1930 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1935 // advance source pointers
1938 // advance destination pointer
1942 // advance source pointer
1943 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1945 // advance destination pointers
1946 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1952 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1954 int x, y, *sax, *say, *csax, *csay;
1956 tColorRGBA *sp, *csp, *csp0, *dp;
1959 // use specialized zoom function when scaling down to exactly half size
1960 if (src->w == 2 * dst->w &&
1961 src->h == 2 * dst->h)
1962 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1965 sx = (float) src->w / (float) dst->w;
1966 sy = (float) src->h / (float) dst->h;
1968 // allocate memory for row increments
1969 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1970 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1972 // precalculate row increments
1973 for (x = 0; x <= dst->w; x++)
1974 *csax++ = (int)(sx * x);
1976 for (y = 0; y <= dst->h; y++)
1977 *csay++ = (int)(sy * y);
1980 sp = csp = csp0 = (tColorRGBA *) src->pixels;
1981 dp = (tColorRGBA *) dst->pixels;
1982 dgap = dst->pitch - dst->w * 4;
1985 for (y = 0; y < dst->h; y++)
1990 for (x = 0; x < dst->w; x++)
1995 // advance source pointers
1999 // advance destination pointer
2003 // advance source pointer
2005 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2007 // advance destination pointers
2008 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2017 // ----------------------------------------------------------------------------
2020 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2021 // ----------------------------------------------------------------------------
2023 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2025 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2026 Uint8 *sp, *dp, *csp;
2030 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2031 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2033 // allocate memory for row increments
2034 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2035 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2037 // precalculate row increments
2040 for (x = 0; x < dst->w; x++)
2043 *csax = (csx >> 16);
2050 for (y = 0; y < dst->h; y++)
2053 *csay = (csy >> 16);
2060 for (x = 0; x < dst->w; x++)
2068 for (y = 0; y < dst->h; y++)
2075 sp = csp = (Uint8 *) src->pixels;
2076 dp = (Uint8 *) dst->pixels;
2077 dgap = dst->pitch - dst->w;
2081 for (y = 0; y < dst->h; y++)
2085 for (x = 0; x < dst->w; x++)
2090 // advance source pointers
2094 // advance destination pointer
2098 // advance source pointer (for row)
2099 csp += ((*csay) * src->pitch);
2102 // advance destination pointers
2112 // ----------------------------------------------------------------------------
2115 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2116 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2117 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2118 // into a 32bit RGBA format on the fly.
2119 // ----------------------------------------------------------------------------
2121 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2123 SDL_Surface *zoom_src = NULL;
2124 SDL_Surface *zoom_dst = NULL;
2125 boolean is_converted = FALSE;
2132 // determine if source surface is 32 bit or 8 bit
2133 is_32bit = (src->format->BitsPerPixel == 32);
2135 if (is_32bit || src->format->BitsPerPixel == 8)
2137 // use source surface 'as is'
2142 // new source surface is 32 bit with a defined RGB ordering
2143 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2144 0x000000ff, 0x0000ff00, 0x00ff0000,
2145 (src->format->Amask ? 0xff000000 : 0));
2146 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2148 is_converted = TRUE;
2151 // allocate surface to completely contain the zoomed surface
2154 // target surface is 32 bit with source RGBA/ABGR ordering
2155 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2156 zoom_src->format->Rmask,
2157 zoom_src->format->Gmask,
2158 zoom_src->format->Bmask,
2159 zoom_src->format->Amask);
2163 // target surface is 8 bit
2164 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2168 // lock source surface
2169 SDL_LockSurface(zoom_src);
2171 // check which kind of surface we have
2174 // call the 32 bit transformation routine to do the zooming
2175 zoomSurfaceRGBA(zoom_src, zoom_dst);
2180 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2181 zoom_dst->format->palette->colors[i] =
2182 zoom_src->format->palette->colors[i];
2183 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2185 // call the 8 bit transformation routine to do the zooming
2186 zoomSurfaceY(zoom_src, zoom_dst);
2189 // unlock source surface
2190 SDL_UnlockSurface(zoom_src);
2192 // free temporary surface
2194 SDL_FreeSurface(zoom_src);
2196 // return destination surface
2200 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2202 SDL_Surface *new_surface;
2204 if (surface == NULL)
2207 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2208 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2210 // remove alpha channel from native non-transparent surface, if defined
2211 SDLSetAlpha(new_surface, FALSE, 0);
2213 // remove transparent color from native non-transparent surface, if defined
2214 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2219 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2221 Bitmap *dst_bitmap = CreateBitmapStruct();
2222 SDL_Surface *src_surface = src_bitmap->surface_masked;
2223 SDL_Surface *dst_surface;
2225 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2226 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2228 dst_bitmap->width = dst_width;
2229 dst_bitmap->height = dst_height;
2231 // create zoomed temporary surface from source surface
2232 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2234 // create native format destination surface from zoomed temporary surface
2235 SDLSetNativeSurface(&dst_surface);
2237 // set color key for zoomed surface from source surface, if defined
2238 if (SDLHasColorKey(src_surface))
2239 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2240 SDLGetColorKey(src_surface));
2242 // create native non-transparent surface for opaque blitting
2243 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2245 // set native transparent surface for masked blitting
2246 dst_bitmap->surface_masked = dst_surface;
2252 // ============================================================================
2253 // load image to bitmap
2254 // ============================================================================
2256 Bitmap *SDLLoadImage(char *filename)
2258 Bitmap *new_bitmap = CreateBitmapStruct();
2259 SDL_Surface *sdl_image_tmp;
2261 if (program.headless)
2263 // prevent sanity check warnings at later stage
2264 new_bitmap->width = new_bitmap->height = 1;
2269 print_timestamp_init("SDLLoadImage");
2271 print_timestamp_time(getBaseNamePtr(filename));
2273 // load image to temporary surface
2274 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2275 Error(ERR_EXIT, "IMG_Load('%s') failed: %s", getBaseNamePtr(filename),
2278 print_timestamp_time("IMG_Load");
2280 UPDATE_BUSY_STATE();
2282 // create native non-transparent surface for current image
2283 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2284 Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2286 print_timestamp_time("SDLGetNativeSurface (opaque)");
2288 UPDATE_BUSY_STATE();
2290 // set black pixel to transparent if no alpha channel / transparent color
2291 if (!SDLHasAlpha(sdl_image_tmp) &&
2292 !SDLHasColorKey(sdl_image_tmp))
2293 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2294 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2296 // create native transparent surface for current image
2297 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2298 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2300 print_timestamp_time("SDLGetNativeSurface (masked)");
2302 UPDATE_BUSY_STATE();
2304 // free temporary surface
2305 SDL_FreeSurface(sdl_image_tmp);
2307 new_bitmap->width = new_bitmap->surface->w;
2308 new_bitmap->height = new_bitmap->surface->h;
2310 print_timestamp_done("SDLLoadImage");
2316 // ----------------------------------------------------------------------------
2317 // custom cursor fuctions
2318 // ----------------------------------------------------------------------------
2320 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2322 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2323 cursor_info->width, cursor_info->height,
2324 cursor_info->hot_x, cursor_info->hot_y);
2327 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2329 static struct MouseCursorInfo *last_cursor_info = NULL;
2330 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2331 static SDL_Cursor *cursor_default = NULL;
2332 static SDL_Cursor *cursor_current = NULL;
2334 // if invoked for the first time, store the SDL default cursor
2335 if (cursor_default == NULL)
2336 cursor_default = SDL_GetCursor();
2338 // only create new cursor if cursor info (custom only) has changed
2339 if (cursor_info != NULL && cursor_info != last_cursor_info)
2341 cursor_current = create_cursor(cursor_info);
2342 last_cursor_info = cursor_info;
2345 // only set new cursor if cursor info (custom or NULL) has changed
2346 if (cursor_info != last_cursor_info2)
2347 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2349 last_cursor_info2 = cursor_info;
2353 // ============================================================================
2355 // ============================================================================
2357 void SDLOpenAudio(void)
2359 if (program.headless)
2362 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2364 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2368 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2369 AUDIO_NUM_CHANNELS_STEREO,
2370 setup.system.audio_fragment_size) < 0)
2372 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2376 audio.sound_available = TRUE;
2377 audio.music_available = TRUE;
2378 audio.loops_available = TRUE;
2379 audio.sound_enabled = TRUE;
2381 // set number of available mixer channels
2382 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2383 audio.music_channel = MUSIC_CHANNEL;
2384 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2386 Mixer_InitChannels();
2389 void SDLCloseAudio(void)
2392 Mix_HaltChannel(-1);
2395 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2399 // ============================================================================
2401 // ============================================================================
2403 void SDLWaitEvent(Event *event)
2405 SDL_WaitEvent(event);
2408 void SDLCorrectRawMousePosition(int *x, int *y)
2410 if (sdl_renderer == NULL)
2413 // this corrects the raw mouse position for logical screen size within event
2414 // filters (correction done later by SDL library when handling mouse events)
2417 float scale_x, scale_y;
2419 SDL_RenderGetViewport(sdl_renderer, &viewport);
2420 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2422 *x = (int)(*x / scale_x);
2423 *y = (int)(*y / scale_y);
2430 // ============================================================================
2431 // joystick functions
2432 // ============================================================================
2434 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2435 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2436 static int sdl_js_axis[MAX_PLAYERS][2];
2437 static int sdl_js_button[MAX_PLAYERS][2];
2438 static boolean sdl_is_controller[MAX_PLAYERS];
2440 void SDLClearJoystickState(void)
2444 for (i = 0; i < MAX_PLAYERS; i++)
2446 for (j = 0; j < 2; j++)
2448 sdl_js_axis_raw[i][j] = -1;
2449 sdl_js_axis[i][j] = 0;
2450 sdl_js_button[i][j] = 0;
2455 boolean SDLOpenJoystick(int nr)
2457 if (nr < 0 || nr >= MAX_PLAYERS)
2460 sdl_is_controller[nr] = SDL_IsGameController(nr);
2463 Error(ERR_DEBUG, "opening joystick %d (%s)",
2464 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2467 if (sdl_is_controller[nr])
2468 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2470 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2472 return (sdl_joystick[nr] != NULL);
2475 void SDLCloseJoystick(int nr)
2477 if (nr < 0 || nr >= MAX_PLAYERS)
2481 Error(ERR_DEBUG, "closing joystick %d", nr);
2484 if (sdl_is_controller[nr])
2485 SDL_GameControllerClose(sdl_joystick[nr]);
2487 SDL_JoystickClose(sdl_joystick[nr]);
2489 sdl_joystick[nr] = NULL;
2492 boolean SDLCheckJoystickOpened(int nr)
2494 if (nr < 0 || nr >= MAX_PLAYERS)
2497 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2500 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2502 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2503 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2504 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2505 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2507 if (nr < 0 || nr >= MAX_PLAYERS)
2513 // prevent (slightly jittering, but centered) axis A from resetting axis B
2514 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2515 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2518 sdl_js_axis[nr][axis_id] = axis_value;
2519 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2522 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2524 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2525 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2526 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2527 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2528 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2529 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2530 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2531 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2534 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2535 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2536 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2537 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2538 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2539 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2540 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2541 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2543 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2544 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2545 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2546 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2547 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2549 if (nr < 0 || nr >= MAX_PLAYERS)
2552 if (button_id == -1)
2555 sdl_js_button[nr][button_id] = button_state;
2558 void HandleJoystickEvent(Event *event)
2560 switch (event->type)
2562 case SDL_CONTROLLERDEVICEADDED:
2564 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2565 event->cdevice.which);
2570 case SDL_CONTROLLERDEVICEREMOVED:
2572 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2573 event->cdevice.which);
2578 case SDL_CONTROLLERAXISMOTION:
2580 Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2581 event->caxis.which, event->caxis.axis, event->caxis.value);
2583 setJoystickAxis(event->caxis.which,
2585 event->caxis.value);
2588 case SDL_CONTROLLERBUTTONDOWN:
2590 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2591 event->cbutton.which, event->cbutton.button);
2593 setJoystickButton(event->cbutton.which,
2594 event->cbutton.button,
2598 case SDL_CONTROLLERBUTTONUP:
2600 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2601 event->cbutton.which, event->cbutton.button);
2603 setJoystickButton(event->cbutton.which,
2604 event->cbutton.button,
2608 case SDL_JOYAXISMOTION:
2609 if (sdl_is_controller[event->jaxis.which])
2613 Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2614 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2616 if (event->jaxis.axis < 4)
2617 setJoystickAxis(event->jaxis.which,
2619 event->jaxis.value);
2622 case SDL_JOYBUTTONDOWN:
2623 if (sdl_is_controller[event->jaxis.which])
2627 Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2628 event->jbutton.which, event->jbutton.button);
2630 if (event->jbutton.button < 4)
2631 setJoystickButton(event->jbutton.which,
2632 event->jbutton.button,
2636 case SDL_JOYBUTTONUP:
2637 if (sdl_is_controller[event->jaxis.which])
2641 Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2642 event->jbutton.which, event->jbutton.button);
2644 if (event->jbutton.button < 4)
2645 setJoystickButton(event->jbutton.which,
2646 event->jbutton.button,
2655 void SDLInitJoysticks(void)
2657 static boolean sdl_joystick_subsystem_initialized = FALSE;
2658 boolean print_warning = !sdl_joystick_subsystem_initialized;
2659 char *mappings_file_base = getPath2(options.conf_directory,
2660 GAMECONTROLLER_BASENAME);
2661 char *mappings_file_user = getPath2(getUserGameDataDir(),
2662 GAMECONTROLLER_BASENAME);
2666 if (!sdl_joystick_subsystem_initialized)
2668 sdl_joystick_subsystem_initialized = TRUE;
2670 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2672 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2674 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2678 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2680 // the included game controller base mappings should always be found
2681 if (num_mappings == -1)
2682 Error(ERR_WARN, "no game controller base mappings found");
2685 Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2688 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2691 // the personal game controller user mappings may or may not be found
2692 if (num_mappings == -1)
2693 Error(ERR_WARN, "no game controller user mappings found");
2695 Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2697 Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2700 checked_free(mappings_file_base);
2701 checked_free(mappings_file_user);
2704 for (i = 0; i < SDL_NumJoysticks(); i++)
2706 const char *name, *type;
2708 if (SDL_IsGameController(i))
2710 name = SDL_GameControllerNameForIndex(i);
2711 type = "game controller";
2715 name = SDL_JoystickNameForIndex(i);
2719 Error(ERR_INFO, "- joystick %d (%s): '%s'",
2720 i, type, (name ? name : "(Unknown)"));
2725 // assign joysticks from configured to connected joystick for all players
2726 for (i = 0; i < MAX_PLAYERS; i++)
2728 // get configured joystick for this player
2729 char *device_name = setup.input[i].joy.device_name;
2730 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2732 if (joystick_nr >= SDL_NumJoysticks())
2734 if (setup.input[i].use_joystick && print_warning)
2735 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2740 // store configured joystick number for each player
2741 joystick.nr[i] = joystick_nr;
2744 // now open all connected joysticks (regardless if configured or not)
2745 for (i = 0; i < SDL_NumJoysticks(); i++)
2747 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2748 if (SDLCheckJoystickOpened(i))
2749 SDLCloseJoystick(i);
2751 if (SDLOpenJoystick(i))
2752 joystick.status = JOYSTICK_ACTIVATED;
2753 else if (print_warning)
2754 Error(ERR_WARN, "cannot open joystick %d", i);
2757 SDLClearJoystickState();
2760 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2762 if (nr < 0 || nr >= MAX_PLAYERS)
2766 *x = sdl_js_axis[nr][0];
2768 *y = sdl_js_axis[nr][1];
2771 *b1 = sdl_js_button[nr][0];
2773 *b2 = sdl_js_button[nr][1];
2779 // ============================================================================
2780 // touch input overlay functions
2781 // ============================================================================
2783 #if defined(USE_TOUCH_INPUT_OVERLAY)
2784 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2787 int grid_xsize = overlay.grid_xsize;
2788 int grid_ysize = overlay.grid_ysize;
2791 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2792 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2794 for (x = 0; x < grid_xsize; x++)
2796 rect.x = (x + 0) * video.screen_width / grid_xsize;
2797 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2799 for (y = 0; y < grid_ysize; y++)
2801 rect.y = (y + 0) * video.screen_height / grid_ysize;
2802 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2804 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2805 SDL_RenderDrawRect(sdl_renderer, &rect);
2809 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2812 static void RenderFillRectangle(int x, int y, int width, int height)
2814 SDL_Rect rect = { x, y, width, height };
2816 SDL_RenderFillRect(sdl_renderer, &rect);
2819 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2821 static int alpha_direction = 0;
2822 static int alpha_highlight = 0;
2823 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2824 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2826 int grid_xsize = overlay.grid_xsize;
2827 int grid_ysize = overlay.grid_ysize;
2830 if (alpha == alpha_max)
2832 if (alpha_direction < 0)
2834 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2836 if (alpha_highlight == 0)
2837 alpha_direction = 1;
2841 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2843 if (alpha_highlight == alpha_max)
2844 alpha_direction = -1;
2849 alpha_direction = 1;
2850 alpha_highlight = alpha;
2853 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2855 for (x = 0; x < grid_xsize; x++)
2857 for (y = 0; y < grid_ysize; y++)
2859 int grid_button = overlay.grid_button[x][y];
2860 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2861 int alpha_draw = alpha;
2862 int outline_border = MV_NONE;
2863 int border_size = 2;
2864 boolean draw_outlined = setup.touch.draw_outlined;
2865 boolean draw_pressed = setup.touch.draw_pressed;
2867 if (grid_button == CHAR_GRID_BUTTON_NONE)
2870 if (grid_button == overlay.grid_button_highlight)
2872 draw_outlined = FALSE;
2873 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2876 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2879 draw_outlined = FALSE;
2881 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2884 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2886 rect.x = (x + 0) * video.screen_width / grid_xsize;
2887 rect.y = (y + 0) * video.screen_height / grid_ysize;
2888 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2889 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2891 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2893 rect.x += border_size;
2894 rect.w -= border_size;
2896 outline_border |= MV_LEFT;
2899 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2901 rect.w -= border_size;
2903 outline_border |= MV_RIGHT;
2906 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2908 rect.y += border_size;
2909 rect.h -= border_size;
2911 outline_border |= MV_UP;
2914 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2916 rect.h -= border_size;
2918 outline_border |= MV_DOWN;
2923 int rect_x = rect.x +
2924 (outline_border & MV_LEFT ? border_size : 0);
2925 int rect_w = rect.w -
2926 (outline_border & MV_LEFT ? border_size : 0) -
2927 (outline_border & MV_RIGHT ? border_size : 0);
2929 if (outline_border & MV_LEFT)
2930 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2932 if (outline_border & MV_RIGHT)
2933 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2934 border_size, rect.h);
2936 if (outline_border & MV_UP)
2937 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2939 if (outline_border & MV_DOWN)
2940 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
2941 rect_w, border_size);
2945 SDL_RenderFillRect(sdl_renderer, &rect);
2950 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2953 static void DrawTouchInputOverlay(void)
2955 static boolean deactivated = TRUE;
2956 static boolean show_grid = FALSE;
2957 static int alpha = 0;
2958 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2959 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2960 boolean active = (overlay.enabled && overlay.active);
2962 if (!active && deactivated)
2967 if (alpha < alpha_max)
2968 alpha = MIN(alpha + alpha_step, alpha_max);
2970 deactivated = FALSE;
2974 alpha = MAX(0, alpha - alpha_step);
2980 if (overlay.show_grid)
2982 else if (deactivated)
2986 DrawTouchInputOverlay_ShowGrid(alpha);
2988 DrawTouchInputOverlay_ShowGridButtons(alpha);
2991 static void DrawTouchGadgetsOverlay(void)
2993 DrawGadgets_OverlayTouchButtons();