1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
19 #define ENABLE_UNUSED_CODE 0 // currently unused functions
21 #define DEBUG_JOYSTICKS 0
24 // ============================================================================
26 // ============================================================================
28 // SDL internal variables
29 static SDL_Window *sdl_window = NULL;
30 static SDL_Renderer *sdl_renderer = NULL;
31 static SDL_Texture *sdl_texture_stream = NULL;
32 static SDL_Texture *sdl_texture_target = NULL;
33 static boolean fullscreen_enabled = FALSE;
34 static boolean limit_screen_updates = FALSE;
37 // functions from SGE library
38 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
40 #if defined(USE_TOUCH_INPUT_OVERLAY)
41 // functions to draw overlay graphics for touch device input
42 static void DrawTouchInputOverlay(void);
43 static void DrawTouchGadgetsOverlay(void);
46 void SDLLimitScreenUpdates(boolean enable)
48 limit_screen_updates = enable;
51 static void FinalizeScreen(int draw_target)
53 // copy global animations to render target buffer, if defined (below border)
54 if (gfx.draw_global_anim_function != NULL)
55 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
57 // copy global masked border to render target buffer, if defined
58 if (gfx.draw_global_border_function != NULL)
59 gfx.draw_global_border_function(draw_target);
61 // copy global animations to render target buffer, if defined (above border)
62 if (gfx.draw_global_anim_function != NULL)
63 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
65 // copy tile selection cursor to render target buffer, if defined (above all)
66 if (gfx.draw_tile_cursor_function != NULL)
67 gfx.draw_tile_cursor_function(draw_target);
70 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
72 static unsigned int update_screen_delay = 0;
73 unsigned int update_screen_delay_value = 50; // (milliseconds)
74 SDL_Surface *screen = backbuffer->surface;
76 if (limit_screen_updates &&
77 !DelayReached(&update_screen_delay, update_screen_delay_value))
80 LimitScreenUpdates(FALSE);
84 static int LastFrameCounter = 0;
85 boolean changed = (FrameCounter != LastFrameCounter);
87 printf("::: FrameCounter == %d [%s]\n", FrameCounter,
88 (changed ? "-" : "SAME FRAME UPDATED"));
90 LastFrameCounter = FrameCounter;
99 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
100 gfx.final_screen_bitmap != NULL) // may not be initialized yet
102 // draw global animations using bitmaps instead of using textures
103 // to prevent texture scaling artefacts (this is potentially slower)
105 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
106 gfx.win_xsize, gfx.win_ysize, 0, 0);
108 FinalizeScreen(DRAW_TO_SCREEN);
110 screen = gfx.final_screen_bitmap->surface;
112 // force full window redraw
116 SDL_Texture *sdl_texture = sdl_texture_stream;
118 // deactivate use of target texture if render targets are not supported
119 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
120 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
121 sdl_texture_target == NULL)
122 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
124 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
125 sdl_texture = sdl_texture_target;
129 int bytes_x = screen->pitch / video.width;
130 int bytes_y = screen->pitch;
132 SDL_UpdateTexture(sdl_texture, rect,
133 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
138 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
141 int xoff = video.screen_xoffset;
142 int yoff = video.screen_yoffset;
143 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
144 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
145 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
147 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
148 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
149 dst_rect2 = &dst_rect_screen;
151 dst_rect1 = &dst_rect_screen;
153 #if defined(HAS_SCREEN_KEYBOARD)
154 if (video.shifted_up || video.shifted_up_delay)
156 int time_current = SDL_GetTicks();
157 int pos = video.shifted_up_pos;
158 int pos_last = video.shifted_up_pos_last;
160 if (!DelayReachedExt(&video.shifted_up_delay, video.shifted_up_delay_value,
163 int delay = time_current - video.shifted_up_delay;
164 int delay_value = video.shifted_up_delay_value;
166 pos = pos_last + (pos - pos_last) * delay / delay_value;
170 video.shifted_up_pos_last = pos;
171 video.shifted_up_delay = 0;
174 SDL_Rect src_rect_up = { 0, pos, video.width, video.height - pos };
175 SDL_Rect dst_rect_up = { xoff, yoff, video.width, video.height - pos };
177 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
178 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
180 src_rect2 = &src_rect_up;
181 dst_rect2 = &dst_rect_up;
185 src_rect1 = &src_rect_up;
186 dst_rect1 = &dst_rect_up;
191 // clear render target buffer
192 SDL_RenderClear(sdl_renderer);
194 // set renderer to use target texture for rendering
195 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
196 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
197 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
199 // copy backbuffer texture to render target buffer
200 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
201 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
203 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
204 FinalizeScreen(DRAW_TO_SCREEN);
206 // when using target texture, copy it to screen buffer
207 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
208 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
210 SDL_SetRenderTarget(sdl_renderer, NULL);
211 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
214 #if defined(USE_TOUCH_INPUT_OVERLAY)
215 // draw overlay graphics for touch device input, if needed
216 DrawTouchInputOverlay();
218 // draw overlay gadgets for touch device input, if needed
219 DrawTouchGadgetsOverlay();
222 // global synchronization point of the game to align video frame delay
223 if (with_frame_delay)
224 WaitUntilDelayReached(&video.frame_delay, video.frame_delay_value);
226 video.frame_counter++;
228 // show render target buffer on screen
229 SDL_RenderPresent(sdl_renderer);
232 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
234 PumpEvents(); // execute event filter actions while waiting
236 UpdateScreenExt(rect, TRUE);
239 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
241 UpdateScreenExt(rect, FALSE);
244 void Delay_WithScreenUpdates(unsigned int delay)
246 unsigned int time_current = SDL_GetTicks();
247 unsigned int time_delayed = time_current + delay;
249 while (time_current < time_delayed)
251 // updating the screen contains waiting for frame delay (non-busy)
252 UpdateScreen_WithFrameDelay(NULL);
254 time_current = SDL_GetTicks();
258 static void SDLSetWindowIcon(char *basename)
260 // (setting the window icon on Mac OS X would replace the high-quality
261 // dock icon with the currently smaller (and uglier) icon from file)
263 #if !defined(PLATFORM_MACOSX)
264 char *filename = getCustomImageFilename(basename);
265 SDL_Surface *surface;
267 if (filename == NULL)
269 Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
274 if ((surface = IMG_Load(filename)) == NULL)
276 Error(ERR_WARN, "IMG_Load('%s') failed: %s", basename, SDL_GetError());
281 // set transparent color
282 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
283 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
285 SDL_SetWindowIcon(sdl_window, surface);
289 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
290 SDL_PixelFormat *format2)
292 return (format1->format == format2->format &&
293 format1->BitsPerPixel == format2->BitsPerPixel &&
294 format1->BytesPerPixel == format2->BytesPerPixel &&
295 format1->Rmask == format2->Rmask &&
296 format1->Gmask == format2->Gmask &&
297 format1->Bmask == format2->Bmask);
300 static Pixel SDLGetColorKey(SDL_Surface *surface)
304 if (SDL_GetColorKey(surface, &color_key) != 0)
310 static boolean SDLHasColorKey(SDL_Surface *surface)
312 return (SDLGetColorKey(surface) != -1);
315 static boolean SDLHasAlpha(SDL_Surface *surface)
317 SDL_BlendMode blend_mode;
319 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
322 return (blend_mode == SDL_BLENDMODE_BLEND);
325 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
327 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
329 SDL_SetSurfaceBlendMode(surface, blend_mode);
330 SDL_SetSurfaceAlphaMod(surface, alpha);
333 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
335 SDL_PixelFormat format;
336 SDL_Surface *new_surface;
341 if (backbuffer && backbuffer->surface)
343 format = *backbuffer->surface->format;
344 format.Amask = surface->format->Amask; // keep alpha channel
348 format = *surface->format;
351 new_surface = SDL_ConvertSurface(surface, &format, 0);
353 if (new_surface == NULL)
354 Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
356 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
357 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
358 SDL_SetColorKey(new_surface, SET_TRANSPARENT_PIXEL,
359 SDLGetColorKey(surface));
364 boolean SDLSetNativeSurface(SDL_Surface **surface)
366 SDL_Surface *new_surface;
368 if (surface == NULL ||
370 backbuffer == NULL ||
371 backbuffer->surface == NULL)
374 // if pixel format already optimized for destination surface, do nothing
375 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
378 new_surface = SDLGetNativeSurface(*surface);
380 SDL_FreeSurface(*surface);
382 *surface = new_surface;
387 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
389 if (program.headless)
392 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
395 Error(ERR_EXIT, "SDL_CreateTextureFromSurface() failed: %s",
401 void SDLCreateBitmapTextures(Bitmap *bitmap)
407 SDL_DestroyTexture(bitmap->texture);
408 if (bitmap->texture_masked)
409 SDL_DestroyTexture(bitmap->texture_masked);
411 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
412 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
415 void SDLFreeBitmapTextures(Bitmap *bitmap)
421 SDL_DestroyTexture(bitmap->texture);
422 if (bitmap->texture_masked)
423 SDL_DestroyTexture(bitmap->texture_masked);
425 bitmap->texture = NULL;
426 bitmap->texture_masked = NULL;
429 void SDLInitVideoDisplay(void)
431 // initialize SDL video
432 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
433 Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
435 // set default SDL depth
436 video.default_depth = 32; // (how to determine video depth in SDL2?)
438 // Code used with SDL 1.2:
439 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
442 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
444 if (program.headless)
447 video.window_scaling_percent = setup.window_scaling_percent;
448 video.window_scaling_quality = setup.window_scaling_quality;
450 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
452 // SDL 2.0: support for (desktop) fullscreen mode available
453 video.fullscreen_available = TRUE;
455 // open SDL video output device (window or fullscreen mode)
456 if (!SDLSetVideoMode(fullscreen))
457 Error(ERR_EXIT, "setting video mode failed");
459 // !!! SDL2 can only set the window icon if the window already exists !!!
461 SDLSetWindowIcon(program.icon_filename);
463 // set window and icon title
467 static void SDLInitVideoBuffer_DrawBuffer(void)
469 /* SDL cannot directly draw to the visible video framebuffer like X11,
470 but always uses a backbuffer, which is then blitted to the visible
471 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
472 visible video framebuffer with 'SDL_Flip', if the hardware supports
473 this). Therefore do not use an additional backbuffer for drawing, but
474 use a symbolic buffer (distinguishable from the SDL backbuffer) called
475 'window', which indicates that the SDL backbuffer should be updated to
476 the visible video framebuffer when attempting to blit to it.
478 For convenience, it seems to be a good idea to create this symbolic
479 buffer 'window' at the same size as the SDL backbuffer. Although it
480 should never be drawn to directly, it would do no harm nevertheless. */
482 // create additional (symbolic) buffer for double-buffering
483 ReCreateBitmap(&window, video.width, video.height);
485 // create dummy drawing buffer for headless mode, if needed
486 if (program.headless)
487 ReCreateBitmap(&backbuffer, video.width, video.height);
490 void SDLInitVideoBuffer(boolean fullscreen)
492 SDLInitVideoBuffer_VideoBuffer(fullscreen);
493 SDLInitVideoBuffer_DrawBuffer();
496 static boolean SDLCreateScreen(boolean fullscreen)
498 SDL_Surface *new_surface = NULL;
500 int surface_flags_window = SURFACE_FLAGS | SDL_WINDOW_RESIZABLE;
501 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
504 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
506 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
507 _without_ enabling 2D/3D acceleration and/or guest additions installed,
508 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
509 it will try to use accelerated graphics and apparently fails miserably) */
510 int renderer_flags = SDL_RENDERER_SOFTWARE;
513 SDLSetScreenSizeAndOffsets(video.width, video.height);
515 int width = video.width;
516 int height = video.height;
517 int screen_width = video.screen_width;
518 int screen_height = video.screen_height;
519 int surface_flags = (fullscreen ? surface_flags_fullscreen :
520 surface_flags_window);
522 // default window size is unscaled
523 video.window_width = screen_width;
524 video.window_height = screen_height;
526 // store if initial screen mode is fullscreen mode when changing screen size
527 video.fullscreen_initial = fullscreen;
529 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
531 video.window_width = window_scaling_factor * screen_width;
532 video.window_height = window_scaling_factor * screen_height;
534 if (sdl_texture_stream)
536 SDL_DestroyTexture(sdl_texture_stream);
537 sdl_texture_stream = NULL;
540 if (sdl_texture_target)
542 SDL_DestroyTexture(sdl_texture_target);
543 sdl_texture_target = NULL;
546 if (!(fullscreen && fullscreen_enabled))
550 SDL_DestroyRenderer(sdl_renderer);
556 SDL_DestroyWindow(sdl_window);
561 if (sdl_window == NULL)
562 sdl_window = SDL_CreateWindow(program.window_title,
563 SDL_WINDOWPOS_CENTERED,
564 SDL_WINDOWPOS_CENTERED,
569 if (sdl_window != NULL)
571 if (sdl_renderer == NULL)
572 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
574 if (sdl_renderer != NULL)
576 SDL_RenderSetLogicalSize(sdl_renderer, screen_width, screen_height);
577 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
578 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
580 SDLSetScreenVsyncMode(setup.vsync_mode);
582 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
583 SDL_PIXELFORMAT_ARGB8888,
584 SDL_TEXTUREACCESS_STREAMING,
587 if (SDL_RenderTargetSupported(sdl_renderer))
588 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
589 SDL_PIXELFORMAT_ARGB8888,
590 SDL_TEXTUREACCESS_TARGET,
593 if (sdl_texture_stream != NULL)
595 // use SDL default values for RGB masks and no alpha channel
596 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
598 if (new_surface == NULL)
599 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
603 Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
608 Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
613 Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
616 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
617 if (new_surface != NULL)
618 fullscreen_enabled = fullscreen;
620 if (backbuffer == NULL)
621 backbuffer = CreateBitmapStruct();
623 backbuffer->width = video.width;
624 backbuffer->height = video.height;
626 if (backbuffer->surface)
627 SDL_FreeSurface(backbuffer->surface);
629 backbuffer->surface = new_surface;
631 return (new_surface != NULL);
634 boolean SDLSetVideoMode(boolean fullscreen)
636 boolean success = FALSE;
640 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
642 // switch display to fullscreen mode, if available
643 success = SDLCreateScreen(TRUE);
647 // switching display to fullscreen mode failed -- do not try it again
648 video.fullscreen_available = FALSE;
652 video.fullscreen_enabled = TRUE;
656 if ((!fullscreen && video.fullscreen_enabled) || !success)
658 // switch display to window mode
659 success = SDLCreateScreen(FALSE);
663 // switching display to window mode failed -- should not happen
667 video.fullscreen_enabled = FALSE;
668 video.window_scaling_percent = setup.window_scaling_percent;
669 video.window_scaling_quality = setup.window_scaling_quality;
671 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
675 SDLRedrawWindow(); // map window
680 void SDLSetWindowTitle(void)
682 if (sdl_window == NULL)
685 SDL_SetWindowTitle(sdl_window, program.window_title);
688 void SDLSetWindowScaling(int window_scaling_percent)
690 if (sdl_window == NULL)
693 float window_scaling_factor = (float)window_scaling_percent / 100;
694 int new_window_width = (int)(window_scaling_factor * video.screen_width);
695 int new_window_height = (int)(window_scaling_factor * video.screen_height);
697 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
699 video.window_scaling_percent = window_scaling_percent;
700 video.window_width = new_window_width;
701 video.window_height = new_window_height;
706 void SDLSetWindowScalingQuality(char *window_scaling_quality)
708 SDL_Texture *new_texture;
710 if (sdl_texture_stream == NULL)
713 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
715 new_texture = SDL_CreateTexture(sdl_renderer,
716 SDL_PIXELFORMAT_ARGB8888,
717 SDL_TEXTUREACCESS_STREAMING,
718 video.width, video.height);
720 if (new_texture != NULL)
722 SDL_DestroyTexture(sdl_texture_stream);
724 sdl_texture_stream = new_texture;
727 if (SDL_RenderTargetSupported(sdl_renderer))
728 new_texture = SDL_CreateTexture(sdl_renderer,
729 SDL_PIXELFORMAT_ARGB8888,
730 SDL_TEXTUREACCESS_TARGET,
731 video.width, video.height);
735 if (new_texture != NULL)
737 SDL_DestroyTexture(sdl_texture_target);
739 sdl_texture_target = new_texture;
744 video.window_scaling_quality = window_scaling_quality;
747 void SDLSetWindowFullscreen(boolean fullscreen)
749 if (sdl_window == NULL)
752 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
754 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
755 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
757 // if screen size was changed in fullscreen mode, correct desktop window size
758 if (!fullscreen && video.fullscreen_initial)
760 SDLSetWindowScaling(setup.window_scaling_percent);
761 SDL_SetWindowPosition(sdl_window,
762 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
764 video.fullscreen_initial = FALSE;
768 void SDLSetDisplaySize(void)
770 SDL_Rect display_bounds;
772 SDL_GetDisplayBounds(0, &display_bounds);
774 video.display_width = display_bounds.w;
775 video.display_height = display_bounds.h;
778 Error(ERR_DEBUG, "SDL real screen size: %d x %d",
779 video.display_width, video.display_height);
783 void SDLSetScreenSizeAndOffsets(int width, int height)
785 // set default video screen size and offsets
786 video.screen_width = width;
787 video.screen_height = height;
788 video.screen_xoffset = 0;
789 video.screen_yoffset = 0;
791 #if defined(USE_COMPLETE_DISPLAY)
792 float ratio_video = (float) width / height;
793 float ratio_display = (float) video.display_width / video.display_height;
795 if (ratio_video != ratio_display)
797 // adjust drawable screen size to cover the whole device display
799 if (ratio_video < ratio_display)
800 video.screen_width *= ratio_display / ratio_video;
802 video.screen_height *= ratio_video / ratio_display;
804 video.screen_xoffset = (video.screen_width - width) / 2;
805 video.screen_yoffset = (video.screen_height - height) / 2;
808 Error(ERR_DEBUG, "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
810 video.screen_width, video.screen_height,
811 ratio_video, ratio_display);
817 void SDLSetScreenSizeForRenderer(int width, int height)
819 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
822 void SDLSetScreenProperties(void)
824 SDLSetScreenSizeAndOffsets(video.width, video.height);
825 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
828 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
830 video.screen_rendering_mode =
831 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
832 SPECIAL_RENDERING_BITMAP :
833 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
834 SPECIAL_RENDERING_TARGET:
835 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
836 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
839 void SDLSetScreenVsyncMode(char *vsync_mode)
842 (strEqual(vsync_mode, STR_VSYNC_MODE_NORMAL) ? VSYNC_MODE_NORMAL :
843 strEqual(vsync_mode, STR_VSYNC_MODE_ADAPTIVE) ? VSYNC_MODE_ADAPTIVE :
845 int result = SDL_GL_SetSwapInterval(interval);
847 // if adaptive vsync requested, but not supported, retry with normal vsync
848 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
849 SDL_GL_SetSwapInterval(VSYNC_MODE_NORMAL);
852 void SDLRedrawWindow(void)
854 UpdateScreen_WithoutFrameDelay(NULL);
857 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
860 if (program.headless)
863 SDL_Surface *surface =
864 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
867 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
869 SDLSetNativeSurface(&surface);
871 bitmap->surface = surface;
874 void SDLFreeBitmapPointers(Bitmap *bitmap)
877 SDL_FreeSurface(bitmap->surface);
878 if (bitmap->surface_masked)
879 SDL_FreeSurface(bitmap->surface_masked);
881 bitmap->surface = NULL;
882 bitmap->surface_masked = NULL;
885 SDL_DestroyTexture(bitmap->texture);
886 if (bitmap->texture_masked)
887 SDL_DestroyTexture(bitmap->texture_masked);
889 bitmap->texture = NULL;
890 bitmap->texture_masked = NULL;
893 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
894 int src_x, int src_y, int width, int height,
895 int dst_x, int dst_y, int mask_mode)
897 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
898 SDL_Rect src_rect, dst_rect;
910 // if (src_bitmap != backbuffer || dst_bitmap != window)
911 if (!(src_bitmap == backbuffer && dst_bitmap == window))
912 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
913 src_bitmap->surface_masked : src_bitmap->surface),
914 &src_rect, real_dst_bitmap->surface, &dst_rect);
916 if (dst_bitmap == window)
917 UpdateScreen_WithFrameDelay(&dst_rect);
920 void SDLBlitTexture(Bitmap *bitmap,
921 int src_x, int src_y, int width, int height,
922 int dst_x, int dst_y, int mask_mode)
924 SDL_Texture *texture;
929 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
944 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
947 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
950 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
958 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
960 if (dst_bitmap == window)
961 UpdateScreen_WithFrameDelay(&rect);
964 void PrepareFadeBitmap(int draw_target)
966 Bitmap *fade_bitmap =
967 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
968 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
970 if (fade_bitmap == NULL)
973 // copy backbuffer to fading buffer
974 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
976 // add border and animations to fading buffer
977 FinalizeScreen(draw_target);
980 void SDLFadeRectangle(int x, int y, int width, int height,
981 int fade_mode, int fade_delay, int post_delay,
982 void (*draw_border_function)(void))
984 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
985 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
986 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
987 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
988 SDL_Surface *surface_screen = backbuffer->surface;
989 SDL_Rect src_rect, dst_rect;
991 int src_x = x, src_y = y;
992 int dst_x = x, dst_y = y;
993 unsigned int time_last, time_current;
995 // store function for drawing global masked border
996 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
998 // deactivate drawing of global border while fading, if needed
999 if (draw_border_function == NULL)
1000 gfx.draw_global_border_function = NULL;
1005 src_rect.h = height;
1009 dst_rect.w = width; // (ignored)
1010 dst_rect.h = height; // (ignored)
1012 dst_rect2 = dst_rect;
1014 // before fading in, store backbuffer (without animation graphics)
1015 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1016 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1018 // copy source and target surfaces to temporary surfaces for fading
1019 if (fade_mode & FADE_TYPE_TRANSFORM)
1021 // (source and target fading buffer already prepared)
1023 else if (fade_mode & FADE_TYPE_FADE_IN)
1025 // (target fading buffer already prepared)
1026 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1028 else // FADE_TYPE_FADE_OUT
1030 // (source fading buffer already prepared)
1031 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1034 time_current = SDL_GetTicks();
1036 if (fade_delay <= 0)
1038 // immediately draw final target frame without delay
1039 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1043 // when fading without delay, also skip post delay
1047 if (fade_mode == FADE_MODE_MELT)
1049 boolean done = FALSE;
1050 int melt_pixels = 2;
1051 int melt_columns = width / melt_pixels;
1052 int ypos[melt_columns];
1053 int max_steps = height / 8 + 32;
1058 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1060 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1062 ypos[0] = -GetSimpleRandom(16);
1064 for (i = 1 ; i < melt_columns; i++)
1066 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1068 ypos[i] = ypos[i - 1] + r;
1081 time_last = time_current;
1082 time_current = SDL_GetTicks();
1083 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1084 steps_final = MIN(MAX(0, steps), max_steps);
1088 done = (steps_done >= steps_final);
1090 for (i = 0 ; i < melt_columns; i++)
1098 else if (ypos[i] < height)
1103 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1105 if (ypos[i] + dy >= height)
1106 dy = height - ypos[i];
1108 // copy part of (appearing) target surface to upper area
1109 src_rect.x = src_x + i * melt_pixels;
1110 // src_rect.y = src_y + ypos[i];
1112 src_rect.w = melt_pixels;
1114 src_rect.h = ypos[i] + dy;
1116 dst_rect.x = dst_x + i * melt_pixels;
1117 // dst_rect.y = dst_y + ypos[i];
1120 if (steps_done >= steps_final)
1121 SDL_BlitSurface(surface_target, &src_rect,
1122 surface_screen, &dst_rect);
1126 // copy part of (disappearing) source surface to lower area
1127 src_rect.x = src_x + i * melt_pixels;
1129 src_rect.w = melt_pixels;
1130 src_rect.h = height - ypos[i];
1132 dst_rect.x = dst_x + i * melt_pixels;
1133 dst_rect.y = dst_y + ypos[i];
1135 if (steps_done >= steps_final)
1136 SDL_BlitSurface(surface_source, &src_rect,
1137 surface_screen, &dst_rect);
1143 src_rect.x = src_x + i * melt_pixels;
1145 src_rect.w = melt_pixels;
1146 src_rect.h = height;
1148 dst_rect.x = dst_x + i * melt_pixels;
1151 if (steps_done >= steps_final)
1152 SDL_BlitSurface(surface_target, &src_rect,
1153 surface_screen, &dst_rect);
1157 if (steps_done >= steps_final)
1159 if (draw_border_function != NULL)
1160 draw_border_function();
1162 UpdateScreen_WithFrameDelay(&dst_rect2);
1166 else if (fade_mode == FADE_MODE_CURTAIN)
1170 int xx_size = width / 2;
1172 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1174 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1176 for (xx = 0; xx < xx_size;)
1178 time_last = time_current;
1179 time_current = SDL_GetTicks();
1180 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1181 xx_final = MIN(MAX(0, xx), xx_size);
1186 src_rect.h = height;
1191 // draw new (target) image to screen buffer
1192 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1194 if (xx_final < xx_size)
1196 src_rect.w = xx_size - xx_final;
1197 src_rect.h = height;
1199 // draw old (source) image to screen buffer (left side)
1201 src_rect.x = src_x + xx_final;
1204 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1206 // draw old (source) image to screen buffer (right side)
1208 src_rect.x = src_x + xx_size;
1209 dst_rect.x = dst_x + xx_size + xx_final;
1211 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1214 if (draw_border_function != NULL)
1215 draw_border_function();
1217 // only update the region of the screen that is affected from fading
1218 UpdateScreen_WithFrameDelay(&dst_rect2);
1221 else // fading in, fading out or cross-fading
1226 for (alpha = 0.0; alpha < 255.0;)
1228 time_last = time_current;
1229 time_current = SDL_GetTicks();
1230 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1231 alpha_final = MIN(MAX(0, alpha), 255);
1233 // draw existing (source) image to screen buffer
1234 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1236 // draw new (target) image to screen buffer using alpha blending
1237 SDLSetAlpha(surface_target, TRUE, alpha_final);
1238 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1240 if (draw_border_function != NULL)
1241 draw_border_function();
1243 // only update the region of the screen that is affected from fading
1244 UpdateScreen_WithFrameDelay(&dst_rect);
1249 Delay_WithScreenUpdates(post_delay);
1251 // restore function for drawing global masked border
1252 gfx.draw_global_border_function = draw_global_border_function;
1254 // after fading in, restore backbuffer (without animation graphics)
1255 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1256 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1259 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1260 int to_x, int to_y, Uint32 color)
1262 SDL_Surface *surface = dst_bitmap->surface;
1266 swap_numbers(&from_x, &to_x);
1269 swap_numbers(&from_y, &to_y);
1273 rect.w = (to_x - from_x + 1);
1274 rect.h = (to_y - from_y + 1);
1276 SDL_FillRect(surface, &rect, color);
1279 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1280 int to_x, int to_y, Uint32 color)
1282 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1285 #if ENABLE_UNUSED_CODE
1286 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1287 int num_points, Uint32 color)
1292 for (i = 0; i < num_points - 1; i++)
1294 for (x = 0; x < line_width; x++)
1296 for (y = 0; y < line_width; y++)
1298 int dx = x - line_width / 2;
1299 int dy = y - line_width / 2;
1301 if ((x == 0 && y == 0) ||
1302 (x == 0 && y == line_width - 1) ||
1303 (x == line_width - 1 && y == 0) ||
1304 (x == line_width - 1 && y == line_width - 1))
1307 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1308 points[i+1].x + dx, points[i+1].y + dy, color);
1315 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1317 SDL_Surface *surface = src_bitmap->surface;
1319 switch (surface->format->BytesPerPixel)
1321 case 1: // assuming 8-bpp
1323 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1327 case 2: // probably 15-bpp or 16-bpp
1329 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1333 case 3: // slow 24-bpp mode; usually not used
1336 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1340 shift = surface->format->Rshift;
1341 color |= *(pix + shift / 8) >> shift;
1342 shift = surface->format->Gshift;
1343 color |= *(pix + shift / 8) >> shift;
1344 shift = surface->format->Bshift;
1345 color |= *(pix + shift / 8) >> shift;
1351 case 4: // probably 32-bpp
1353 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1362 // ============================================================================
1363 // The following functions were taken from the SGE library
1364 // (SDL Graphics Extension Library) by Anders Lindström
1365 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1366 // ============================================================================
1368 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1370 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1372 switch (surface->format->BytesPerPixel)
1377 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1383 // Probably 15-bpp or 16-bpp
1384 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1390 // Slow 24-bpp mode, usually not used
1394 // Gack - slow, but endian correct
1395 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1396 shift = surface->format->Rshift;
1397 *(pix+shift/8) = color>>shift;
1398 shift = surface->format->Gshift;
1399 *(pix+shift/8) = color>>shift;
1400 shift = surface->format->Bshift;
1401 *(pix+shift/8) = color>>shift;
1408 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1416 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1417 Uint8 R, Uint8 G, Uint8 B)
1419 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1422 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1424 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1427 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1429 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1432 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1437 // Gack - slow, but endian correct
1438 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1439 shift = surface->format->Rshift;
1440 *(pix+shift/8) = color>>shift;
1441 shift = surface->format->Gshift;
1442 *(pix+shift/8) = color>>shift;
1443 shift = surface->format->Bshift;
1444 *(pix+shift/8) = color>>shift;
1447 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1449 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1452 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1454 switch (dest->format->BytesPerPixel)
1457 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1461 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1465 _PutPixel24(dest,x,y,color);
1469 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1475 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1477 if (SDL_MUSTLOCK(surface))
1479 if (SDL_LockSurface(surface) < 0)
1485 _PutPixel(surface, x, y, color);
1487 if (SDL_MUSTLOCK(surface))
1489 SDL_UnlockSurface(surface);
1494 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1495 Uint8 r, Uint8 g, Uint8 b)
1497 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1500 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1502 if (y >= 0 && y <= dest->h - 1)
1504 switch (dest->format->BytesPerPixel)
1507 return y*dest->pitch;
1511 return y*dest->pitch/2;
1515 return y*dest->pitch;
1519 return y*dest->pitch/4;
1527 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1530 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1532 switch (surface->format->BytesPerPixel)
1537 *((Uint8 *)surface->pixels + ypitch + x) = color;
1543 // Probably 15-bpp or 16-bpp
1544 *((Uint16 *)surface->pixels + ypitch + x) = color;
1550 // Slow 24-bpp mode, usually not used
1554 // Gack - slow, but endian correct
1555 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1556 shift = surface->format->Rshift;
1557 *(pix+shift/8) = color>>shift;
1558 shift = surface->format->Gshift;
1559 *(pix+shift/8) = color>>shift;
1560 shift = surface->format->Bshift;
1561 *(pix+shift/8) = color>>shift;
1568 *((Uint32 *)surface->pixels + ypitch + x) = color;
1575 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1580 if (SDL_MUSTLOCK(Surface))
1582 if (SDL_LockSurface(Surface) < 0)
1596 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1600 if (x2 > Surface->w - 1)
1601 x2 = Surface->w - 1;
1608 SDL_FillRect(Surface, &l, Color);
1610 if (SDL_MUSTLOCK(Surface))
1612 SDL_UnlockSurface(Surface);
1616 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1617 Uint8 R, Uint8 G, Uint8 B)
1619 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1622 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1635 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1639 if (x2 > Surface->w - 1)
1640 x2 = Surface->w - 1;
1647 SDL_FillRect(Surface, &l, Color);
1650 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1655 if (SDL_MUSTLOCK(Surface))
1657 if (SDL_LockSurface(Surface) < 0)
1671 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1675 if (y2 > Surface->h - 1)
1676 y2 = Surface->h - 1;
1683 SDL_FillRect(Surface, &l, Color);
1685 if (SDL_MUSTLOCK(Surface))
1687 SDL_UnlockSurface(Surface);
1691 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1692 Uint8 R, Uint8 G, Uint8 B)
1694 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1697 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1710 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1714 if (y2 > Surface->h - 1)
1715 y2 = Surface->h - 1;
1722 SDL_FillRect(Surface, &l, Color);
1726 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1727 Sint16 x2, Sint16 y2, Uint32 Color,
1728 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1731 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1736 sdx = (dx < 0) ? -1 : 1;
1737 sdy = (dy < 0) ? -1 : 1;
1749 for (x = 0; x < dx; x++)
1751 Callback(Surface, px, py, Color);
1765 for (y = 0; y < dy; y++)
1767 Callback(Surface, px, py, Color);
1782 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1783 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1784 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1787 sge_DoLine(Surface, X1, Y1, X2, Y2,
1788 SDL_MapRGB(Surface->format, R, G, B), Callback);
1792 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1795 if (SDL_MUSTLOCK(Surface))
1797 if (SDL_LockSurface(Surface) < 0)
1802 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1804 // unlock the display
1805 if (SDL_MUSTLOCK(Surface))
1807 SDL_UnlockSurface(Surface);
1812 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1813 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1815 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1819 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1821 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1825 // ----------------------------------------------------------------------------
1826 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1827 // ----------------------------------------------------------------------------
1829 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1830 int width, int height, Uint32 color)
1834 for (y = src_y; y < src_y + height; y++)
1836 for (x = src_x; x < src_x + width; x++)
1838 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1840 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1845 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1846 int src_x, int src_y, int width, int height,
1847 int dst_x, int dst_y)
1851 for (y = 0; y < height; y++)
1853 for (x = 0; x < width; x++)
1855 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1857 if (pixel != BLACK_PIXEL)
1858 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1864 // ============================================================================
1865 // The following functions were taken from the SDL_gfx library version 2.0.3
1866 // (Rotozoomer) by Andreas Schiffler
1867 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1868 // ============================================================================
1870 // ----------------------------------------------------------------------------
1873 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1874 // ----------------------------------------------------------------------------
1884 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1887 tColorRGBA *sp, *csp, *dp;
1891 sp = csp = (tColorRGBA *) src->pixels;
1892 dp = (tColorRGBA *) dst->pixels;
1893 dgap = dst->pitch - dst->w * 4;
1895 for (y = 0; y < dst->h; y++)
1899 for (x = 0; x < dst->w; x++)
1901 tColorRGBA *sp0 = sp;
1902 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1903 tColorRGBA *sp00 = &sp0[0];
1904 tColorRGBA *sp01 = &sp0[1];
1905 tColorRGBA *sp10 = &sp1[0];
1906 tColorRGBA *sp11 = &sp1[1];
1909 // create new color pixel from all four source color pixels
1910 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1911 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1912 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1913 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1918 // advance source pointers
1921 // advance destination pointer
1925 // advance source pointer
1926 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1928 // advance destination pointers
1929 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1935 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1937 int x, y, *sax, *say, *csax, *csay;
1939 tColorRGBA *sp, *csp, *csp0, *dp;
1942 // use specialized zoom function when scaling down to exactly half size
1943 if (src->w == 2 * dst->w &&
1944 src->h == 2 * dst->h)
1945 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1948 sx = (float) src->w / (float) dst->w;
1949 sy = (float) src->h / (float) dst->h;
1951 // allocate memory for row increments
1952 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1953 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1955 // precalculate row increments
1956 for (x = 0; x <= dst->w; x++)
1957 *csax++ = (int)(sx * x);
1959 for (y = 0; y <= dst->h; y++)
1960 *csay++ = (int)(sy * y);
1963 sp = csp = csp0 = (tColorRGBA *) src->pixels;
1964 dp = (tColorRGBA *) dst->pixels;
1965 dgap = dst->pitch - dst->w * 4;
1968 for (y = 0; y < dst->h; y++)
1973 for (x = 0; x < dst->w; x++)
1978 // advance source pointers
1982 // advance destination pointer
1986 // advance source pointer
1988 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
1990 // advance destination pointers
1991 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2000 // ----------------------------------------------------------------------------
2003 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2004 // ----------------------------------------------------------------------------
2006 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2008 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2009 Uint8 *sp, *dp, *csp;
2013 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2014 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2016 // allocate memory for row increments
2017 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2018 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2020 // precalculate row increments
2023 for (x = 0; x < dst->w; x++)
2026 *csax = (csx >> 16);
2033 for (y = 0; y < dst->h; y++)
2036 *csay = (csy >> 16);
2043 for (x = 0; x < dst->w; x++)
2051 for (y = 0; y < dst->h; y++)
2058 sp = csp = (Uint8 *) src->pixels;
2059 dp = (Uint8 *) dst->pixels;
2060 dgap = dst->pitch - dst->w;
2064 for (y = 0; y < dst->h; y++)
2068 for (x = 0; x < dst->w; x++)
2073 // advance source pointers
2077 // advance destination pointer
2081 // advance source pointer (for row)
2082 csp += ((*csay) * src->pitch);
2085 // advance destination pointers
2095 // ----------------------------------------------------------------------------
2098 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2099 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2100 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2101 // into a 32bit RGBA format on the fly.
2102 // ----------------------------------------------------------------------------
2104 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2106 SDL_Surface *zoom_src = NULL;
2107 SDL_Surface *zoom_dst = NULL;
2108 boolean is_converted = FALSE;
2115 // determine if source surface is 32 bit or 8 bit
2116 is_32bit = (src->format->BitsPerPixel == 32);
2118 if (is_32bit || src->format->BitsPerPixel == 8)
2120 // use source surface 'as is'
2125 // new source surface is 32 bit with a defined RGB ordering
2126 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2127 0x000000ff, 0x0000ff00, 0x00ff0000,
2128 (src->format->Amask ? 0xff000000 : 0));
2129 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2131 is_converted = TRUE;
2134 // allocate surface to completely contain the zoomed surface
2137 // target surface is 32 bit with source RGBA/ABGR ordering
2138 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2139 zoom_src->format->Rmask,
2140 zoom_src->format->Gmask,
2141 zoom_src->format->Bmask,
2142 zoom_src->format->Amask);
2146 // target surface is 8 bit
2147 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2151 // lock source surface
2152 SDL_LockSurface(zoom_src);
2154 // check which kind of surface we have
2157 // call the 32 bit transformation routine to do the zooming
2158 zoomSurfaceRGBA(zoom_src, zoom_dst);
2163 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2164 zoom_dst->format->palette->colors[i] =
2165 zoom_src->format->palette->colors[i];
2166 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2168 // call the 8 bit transformation routine to do the zooming
2169 zoomSurfaceY(zoom_src, zoom_dst);
2172 // unlock source surface
2173 SDL_UnlockSurface(zoom_src);
2175 // free temporary surface
2177 SDL_FreeSurface(zoom_src);
2179 // return destination surface
2183 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2185 SDL_Surface *new_surface;
2187 if (surface == NULL)
2190 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2191 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2193 // remove alpha channel from native non-transparent surface, if defined
2194 SDLSetAlpha(new_surface, FALSE, 0);
2196 // remove transparent color from native non-transparent surface, if defined
2197 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2202 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2204 Bitmap *dst_bitmap = CreateBitmapStruct();
2205 SDL_Surface *src_surface = src_bitmap->surface_masked;
2206 SDL_Surface *dst_surface;
2208 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2209 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2211 dst_bitmap->width = dst_width;
2212 dst_bitmap->height = dst_height;
2214 // create zoomed temporary surface from source surface
2215 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2217 // create native format destination surface from zoomed temporary surface
2218 SDLSetNativeSurface(&dst_surface);
2220 // set color key for zoomed surface from source surface, if defined
2221 if (SDLHasColorKey(src_surface))
2222 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2223 SDLGetColorKey(src_surface));
2225 // create native non-transparent surface for opaque blitting
2226 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2228 // set native transparent surface for masked blitting
2229 dst_bitmap->surface_masked = dst_surface;
2235 // ============================================================================
2236 // load image to bitmap
2237 // ============================================================================
2239 Bitmap *SDLLoadImage(char *filename)
2241 Bitmap *new_bitmap = CreateBitmapStruct();
2242 SDL_Surface *sdl_image_tmp;
2244 if (program.headless)
2246 // prevent sanity check warnings at later stage
2247 new_bitmap->width = new_bitmap->height = 1;
2252 print_timestamp_init("SDLLoadImage");
2254 print_timestamp_time(getBaseNamePtr(filename));
2256 // load image to temporary surface
2257 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2258 Error(ERR_EXIT, "IMG_Load('%s') failed: %s", getBaseNamePtr(filename),
2261 print_timestamp_time("IMG_Load");
2263 UPDATE_BUSY_STATE();
2265 // create native non-transparent surface for current image
2266 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2267 Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2269 print_timestamp_time("SDLGetNativeSurface (opaque)");
2271 UPDATE_BUSY_STATE();
2273 // set black pixel to transparent if no alpha channel / transparent color
2274 if (!SDLHasAlpha(sdl_image_tmp) &&
2275 !SDLHasColorKey(sdl_image_tmp))
2276 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2277 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2279 // create native transparent surface for current image
2280 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2281 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2283 print_timestamp_time("SDLGetNativeSurface (masked)");
2285 UPDATE_BUSY_STATE();
2287 // free temporary surface
2288 SDL_FreeSurface(sdl_image_tmp);
2290 new_bitmap->width = new_bitmap->surface->w;
2291 new_bitmap->height = new_bitmap->surface->h;
2293 print_timestamp_done("SDLLoadImage");
2299 // ----------------------------------------------------------------------------
2300 // custom cursor fuctions
2301 // ----------------------------------------------------------------------------
2303 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2305 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2306 cursor_info->width, cursor_info->height,
2307 cursor_info->hot_x, cursor_info->hot_y);
2310 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2312 static struct MouseCursorInfo *last_cursor_info = NULL;
2313 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2314 static SDL_Cursor *cursor_default = NULL;
2315 static SDL_Cursor *cursor_current = NULL;
2317 // if invoked for the first time, store the SDL default cursor
2318 if (cursor_default == NULL)
2319 cursor_default = SDL_GetCursor();
2321 // only create new cursor if cursor info (custom only) has changed
2322 if (cursor_info != NULL && cursor_info != last_cursor_info)
2324 cursor_current = create_cursor(cursor_info);
2325 last_cursor_info = cursor_info;
2328 // only set new cursor if cursor info (custom or NULL) has changed
2329 if (cursor_info != last_cursor_info2)
2330 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2332 last_cursor_info2 = cursor_info;
2336 // ============================================================================
2338 // ============================================================================
2340 void SDLOpenAudio(void)
2342 if (program.headless)
2345 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2347 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2351 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2352 AUDIO_NUM_CHANNELS_STEREO,
2353 setup.system.audio_fragment_size) < 0)
2355 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2359 audio.sound_available = TRUE;
2360 audio.music_available = TRUE;
2361 audio.loops_available = TRUE;
2362 audio.sound_enabled = TRUE;
2364 // set number of available mixer channels
2365 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2366 audio.music_channel = MUSIC_CHANNEL;
2367 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2369 Mixer_InitChannels();
2372 void SDLCloseAudio(void)
2375 Mix_HaltChannel(-1);
2378 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2382 // ============================================================================
2384 // ============================================================================
2386 void SDLWaitEvent(Event *event)
2388 SDL_WaitEvent(event);
2391 void SDLCorrectRawMousePosition(int *x, int *y)
2393 if (sdl_renderer == NULL)
2396 // this corrects the raw mouse position for logical screen size within event
2397 // filters (correction done later by SDL library when handling mouse events)
2400 float scale_x, scale_y;
2402 SDL_RenderGetViewport(sdl_renderer, &viewport);
2403 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2405 *x = (int)(*x / scale_x);
2406 *y = (int)(*y / scale_y);
2413 // ============================================================================
2414 // joystick functions
2415 // ============================================================================
2417 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2418 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2419 static int sdl_js_axis[MAX_PLAYERS][2];
2420 static int sdl_js_button[MAX_PLAYERS][2];
2421 static boolean sdl_is_controller[MAX_PLAYERS];
2423 void SDLClearJoystickState(void)
2427 for (i = 0; i < MAX_PLAYERS; i++)
2429 for (j = 0; j < 2; j++)
2431 sdl_js_axis_raw[i][j] = -1;
2432 sdl_js_axis[i][j] = 0;
2433 sdl_js_button[i][j] = 0;
2438 boolean SDLOpenJoystick(int nr)
2440 if (nr < 0 || nr >= MAX_PLAYERS)
2443 sdl_is_controller[nr] = SDL_IsGameController(nr);
2446 Error(ERR_DEBUG, "opening joystick %d (%s)",
2447 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2450 if (sdl_is_controller[nr])
2451 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2453 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2455 return (sdl_joystick[nr] != NULL);
2458 void SDLCloseJoystick(int nr)
2460 if (nr < 0 || nr >= MAX_PLAYERS)
2464 Error(ERR_DEBUG, "closing joystick %d", nr);
2467 if (sdl_is_controller[nr])
2468 SDL_GameControllerClose(sdl_joystick[nr]);
2470 SDL_JoystickClose(sdl_joystick[nr]);
2472 sdl_joystick[nr] = NULL;
2475 boolean SDLCheckJoystickOpened(int nr)
2477 if (nr < 0 || nr >= MAX_PLAYERS)
2480 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2483 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2485 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2486 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2487 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2488 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2490 if (nr < 0 || nr >= MAX_PLAYERS)
2496 // prevent (slightly jittering, but centered) axis A from resetting axis B
2497 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2498 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2501 sdl_js_axis[nr][axis_id] = axis_value;
2502 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2505 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2507 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2508 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2509 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2510 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2511 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2512 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2513 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2514 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2517 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2518 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2519 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2520 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2521 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2522 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2523 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2524 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2526 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2527 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2528 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2529 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2530 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2532 if (nr < 0 || nr >= MAX_PLAYERS)
2535 if (button_id == -1)
2538 sdl_js_button[nr][button_id] = button_state;
2541 void HandleJoystickEvent(Event *event)
2543 switch (event->type)
2545 case SDL_CONTROLLERDEVICEADDED:
2547 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2548 event->cdevice.which);
2553 case SDL_CONTROLLERDEVICEREMOVED:
2555 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2556 event->cdevice.which);
2561 case SDL_CONTROLLERAXISMOTION:
2563 Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2564 event->caxis.which, event->caxis.axis, event->caxis.value);
2566 setJoystickAxis(event->caxis.which,
2568 event->caxis.value);
2571 case SDL_CONTROLLERBUTTONDOWN:
2573 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2574 event->cbutton.which, event->cbutton.button);
2576 setJoystickButton(event->cbutton.which,
2577 event->cbutton.button,
2581 case SDL_CONTROLLERBUTTONUP:
2583 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2584 event->cbutton.which, event->cbutton.button);
2586 setJoystickButton(event->cbutton.which,
2587 event->cbutton.button,
2591 case SDL_JOYAXISMOTION:
2592 if (sdl_is_controller[event->jaxis.which])
2596 Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2597 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2599 if (event->jaxis.axis < 4)
2600 setJoystickAxis(event->jaxis.which,
2602 event->jaxis.value);
2605 case SDL_JOYBUTTONDOWN:
2606 if (sdl_is_controller[event->jaxis.which])
2610 Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2611 event->jbutton.which, event->jbutton.button);
2613 if (event->jbutton.button < 4)
2614 setJoystickButton(event->jbutton.which,
2615 event->jbutton.button,
2619 case SDL_JOYBUTTONUP:
2620 if (sdl_is_controller[event->jaxis.which])
2624 Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2625 event->jbutton.which, event->jbutton.button);
2627 if (event->jbutton.button < 4)
2628 setJoystickButton(event->jbutton.which,
2629 event->jbutton.button,
2638 void SDLInitJoysticks(void)
2640 static boolean sdl_joystick_subsystem_initialized = FALSE;
2641 boolean print_warning = !sdl_joystick_subsystem_initialized;
2642 char *mappings_file_base = getPath2(options.conf_directory,
2643 GAMECONTROLLER_BASENAME);
2644 char *mappings_file_user = getPath2(getUserGameDataDir(),
2645 GAMECONTROLLER_BASENAME);
2649 if (!sdl_joystick_subsystem_initialized)
2651 sdl_joystick_subsystem_initialized = TRUE;
2653 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2655 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2657 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2661 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2663 // the included game controller base mappings should always be found
2664 if (num_mappings == -1)
2665 Error(ERR_WARN, "no game controller base mappings found");
2668 Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2671 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2674 // the personal game controller user mappings may or may not be found
2675 if (num_mappings == -1)
2676 Error(ERR_WARN, "no game controller user mappings found");
2678 Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2680 Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2683 checked_free(mappings_file_base);
2684 checked_free(mappings_file_user);
2687 for (i = 0; i < SDL_NumJoysticks(); i++)
2689 const char *name, *type;
2691 if (SDL_IsGameController(i))
2693 name = SDL_GameControllerNameForIndex(i);
2694 type = "game controller";
2698 name = SDL_JoystickNameForIndex(i);
2702 Error(ERR_INFO, "- joystick %d (%s): '%s'",
2703 i, type, (name ? name : "(Unknown)"));
2708 // assign joysticks from configured to connected joystick for all players
2709 for (i = 0; i < MAX_PLAYERS; i++)
2711 // get configured joystick for this player
2712 char *device_name = setup.input[i].joy.device_name;
2713 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2715 if (joystick_nr >= SDL_NumJoysticks())
2717 if (setup.input[i].use_joystick && print_warning)
2718 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2723 // store configured joystick number for each player
2724 joystick.nr[i] = joystick_nr;
2727 // now open all connected joysticks (regardless if configured or not)
2728 for (i = 0; i < SDL_NumJoysticks(); i++)
2730 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2731 if (SDLCheckJoystickOpened(i))
2732 SDLCloseJoystick(i);
2734 if (SDLOpenJoystick(i))
2735 joystick.status = JOYSTICK_ACTIVATED;
2736 else if (print_warning)
2737 Error(ERR_WARN, "cannot open joystick %d", i);
2740 SDLClearJoystickState();
2743 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2745 if (nr < 0 || nr >= MAX_PLAYERS)
2749 *x = sdl_js_axis[nr][0];
2751 *y = sdl_js_axis[nr][1];
2754 *b1 = sdl_js_button[nr][0];
2756 *b2 = sdl_js_button[nr][1];
2762 // ============================================================================
2763 // touch input overlay functions
2764 // ============================================================================
2766 #if defined(USE_TOUCH_INPUT_OVERLAY)
2767 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2770 int grid_xsize = overlay.grid_xsize;
2771 int grid_ysize = overlay.grid_ysize;
2774 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2775 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2777 for (x = 0; x < grid_xsize; x++)
2779 rect.x = (x + 0) * video.screen_width / grid_xsize;
2780 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2782 for (y = 0; y < grid_ysize; y++)
2784 rect.y = (y + 0) * video.screen_height / grid_ysize;
2785 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2787 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2788 SDL_RenderDrawRect(sdl_renderer, &rect);
2792 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2795 static void RenderFillRectangle(int x, int y, int width, int height)
2797 SDL_Rect rect = { x, y, width, height };
2799 SDL_RenderFillRect(sdl_renderer, &rect);
2802 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2804 static int alpha_direction = 0;
2805 static int alpha_highlight = 0;
2806 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2807 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2809 int grid_xsize = overlay.grid_xsize;
2810 int grid_ysize = overlay.grid_ysize;
2813 if (alpha == alpha_max)
2815 if (alpha_direction < 0)
2817 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2819 if (alpha_highlight == 0)
2820 alpha_direction = 1;
2824 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2826 if (alpha_highlight == alpha_max)
2827 alpha_direction = -1;
2832 alpha_direction = 1;
2833 alpha_highlight = alpha;
2836 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2838 for (x = 0; x < grid_xsize; x++)
2840 for (y = 0; y < grid_ysize; y++)
2842 int grid_button = overlay.grid_button[x][y];
2843 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2844 int alpha_draw = alpha;
2845 int outline_border = MV_NONE;
2846 int border_size = 2;
2847 boolean draw_outlined = setup.touch.draw_outlined;
2848 boolean draw_pressed = setup.touch.draw_pressed;
2850 if (grid_button == CHAR_GRID_BUTTON_NONE)
2853 if (grid_button == overlay.grid_button_highlight)
2855 draw_outlined = FALSE;
2856 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2859 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2862 draw_outlined = FALSE;
2864 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2867 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2869 rect.x = (x + 0) * video.screen_width / grid_xsize;
2870 rect.y = (y + 0) * video.screen_height / grid_ysize;
2871 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2872 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2874 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2876 rect.x += border_size;
2877 rect.w -= border_size;
2879 outline_border |= MV_LEFT;
2882 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2884 rect.w -= border_size;
2886 outline_border |= MV_RIGHT;
2889 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2891 rect.y += border_size;
2892 rect.h -= border_size;
2894 outline_border |= MV_UP;
2897 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2899 rect.h -= border_size;
2901 outline_border |= MV_DOWN;
2906 int rect_x = rect.x +
2907 (outline_border & MV_LEFT ? border_size : 0);
2908 int rect_w = rect.w -
2909 (outline_border & MV_LEFT ? border_size : 0) -
2910 (outline_border & MV_RIGHT ? border_size : 0);
2912 if (outline_border & MV_LEFT)
2913 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2915 if (outline_border & MV_RIGHT)
2916 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2917 border_size, rect.h);
2919 if (outline_border & MV_UP)
2920 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2922 if (outline_border & MV_DOWN)
2923 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
2924 rect_w, border_size);
2928 SDL_RenderFillRect(sdl_renderer, &rect);
2933 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2936 static void DrawTouchInputOverlay(void)
2938 static boolean deactivated = TRUE;
2939 static boolean show_grid = FALSE;
2940 static int alpha = 0;
2941 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2942 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2943 boolean active = (overlay.enabled && overlay.active);
2945 if (!active && deactivated)
2950 if (alpha < alpha_max)
2951 alpha = MIN(alpha + alpha_step, alpha_max);
2953 deactivated = FALSE;
2957 alpha = MAX(0, alpha - alpha_step);
2963 if (overlay.show_grid)
2965 else if (deactivated)
2969 DrawTouchInputOverlay_ShowGrid(alpha);
2971 DrawTouchInputOverlay_ShowGridButtons(alpha);
2974 static void DrawTouchGadgetsOverlay(void)
2976 DrawGadgets_OverlayTouchButtons();