1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
18 #define ENABLE_UNUSED_CODE 0 // currently unused functions
20 #define DEBUG_JOYSTICKS 0
23 // ============================================================================
25 // ============================================================================
27 // SDL internal variables
28 static SDL_Window *sdl_window = NULL;
29 static SDL_Renderer *sdl_renderer = NULL;
30 static SDL_Texture *sdl_texture_stream = NULL;
31 static SDL_Texture *sdl_texture_target = NULL;
32 static boolean fullscreen_enabled = FALSE;
33 static boolean limit_screen_updates = FALSE;
36 // functions from SGE library
37 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
39 #if defined(USE_TOUCH_INPUT_OVERLAY)
40 // functions to draw overlay graphics for touch device input
41 static void DrawTouchInputOverlay(void);
44 void SDLLimitScreenUpdates(boolean enable)
46 limit_screen_updates = enable;
49 static void FinalizeScreen(int draw_target)
51 // copy global animations to render target buffer, if defined (below border)
52 if (gfx.draw_global_anim_function != NULL)
53 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
55 // copy global masked border to render target buffer, if defined
56 if (gfx.draw_global_border_function != NULL)
57 gfx.draw_global_border_function(draw_target);
59 // copy global animations to render target buffer, if defined (above border)
60 if (gfx.draw_global_anim_function != NULL)
61 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
63 // copy tile selection cursor to render target buffer, if defined (above all)
64 if (gfx.draw_tile_cursor_function != NULL)
65 gfx.draw_tile_cursor_function(draw_target);
68 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
70 static unsigned int update_screen_delay = 0;
71 unsigned int update_screen_delay_value = 50; // (milliseconds)
72 SDL_Surface *screen = backbuffer->surface;
74 if (limit_screen_updates &&
75 !DelayReached(&update_screen_delay, update_screen_delay_value))
78 LimitScreenUpdates(FALSE);
82 static int LastFrameCounter = 0;
83 boolean changed = (FrameCounter != LastFrameCounter);
85 printf("::: FrameCounter == %d [%s]\n", FrameCounter,
86 (changed ? "-" : "SAME FRAME UPDATED"));
88 LastFrameCounter = FrameCounter;
97 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
98 gfx.final_screen_bitmap != NULL) // may not be initialized yet
100 // draw global animations using bitmaps instead of using textures
101 // to prevent texture scaling artefacts (this is potentially slower)
103 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
104 gfx.win_xsize, gfx.win_ysize, 0, 0);
106 FinalizeScreen(DRAW_TO_SCREEN);
108 screen = gfx.final_screen_bitmap->surface;
110 // force full window redraw
114 SDL_Texture *sdl_texture = sdl_texture_stream;
116 // deactivate use of target texture if render targets are not supported
117 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
118 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
119 sdl_texture_target == NULL)
120 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
122 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
123 sdl_texture = sdl_texture_target;
127 int bytes_x = screen->pitch / video.width;
128 int bytes_y = screen->pitch;
130 SDL_UpdateTexture(sdl_texture, rect,
131 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
136 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
139 int xoff = video.screen_xoffset;
140 int yoff = video.screen_yoffset;
141 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
142 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
143 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
145 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
146 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
147 dst_rect2 = &dst_rect_screen;
149 dst_rect1 = &dst_rect_screen;
151 #if defined(HAS_SCREEN_KEYBOARD)
152 if (video.shifted_up || video.shifted_up_delay)
154 int time_current = SDL_GetTicks();
155 int pos = video.shifted_up_pos;
156 int pos_last = video.shifted_up_pos_last;
158 if (!DelayReachedExt(&video.shifted_up_delay, video.shifted_up_delay_value,
161 int delay = time_current - video.shifted_up_delay;
162 int delay_value = video.shifted_up_delay_value;
164 pos = pos_last + (pos - pos_last) * delay / delay_value;
168 video.shifted_up_pos_last = pos;
169 video.shifted_up_delay = 0;
172 SDL_Rect src_rect_up = { 0, pos, video.width, video.height - pos };
173 SDL_Rect dst_rect_up = { xoff, yoff, video.width, video.height - pos };
175 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
176 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
178 src_rect2 = &src_rect_up;
179 dst_rect2 = &dst_rect_up;
183 src_rect1 = &src_rect_up;
184 dst_rect1 = &dst_rect_up;
189 // clear render target buffer
190 SDL_RenderClear(sdl_renderer);
192 // set renderer to use target texture for rendering
193 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
194 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
195 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
197 // copy backbuffer texture to render target buffer
198 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
199 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
201 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
202 FinalizeScreen(DRAW_TO_SCREEN);
204 // when using target texture, copy it to screen buffer
205 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
206 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
208 SDL_SetRenderTarget(sdl_renderer, NULL);
209 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
212 #if defined(USE_TOUCH_INPUT_OVERLAY)
213 // draw overlay graphics for touch device input, if needed
214 DrawTouchInputOverlay();
217 // global synchronization point of the game to align video frame delay
218 if (with_frame_delay)
219 WaitUntilDelayReached(&video.frame_delay, video.frame_delay_value);
221 // show render target buffer on screen
222 SDL_RenderPresent(sdl_renderer);
225 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
227 UpdateScreenExt(rect, TRUE);
230 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
232 UpdateScreenExt(rect, FALSE);
235 void Delay_WithScreenUpdates(unsigned int delay)
237 unsigned int time_current = SDL_GetTicks();
238 unsigned int time_delayed = time_current + delay;
240 while (time_current < time_delayed)
242 // updating the screen contains waiting for frame delay (non-busy)
243 UpdateScreen_WithFrameDelay(NULL);
245 time_current = SDL_GetTicks();
249 static void SDLSetWindowIcon(char *basename)
251 // (setting the window icon on Mac OS X would replace the high-quality
252 // dock icon with the currently smaller (and uglier) icon from file)
254 #if !defined(PLATFORM_MACOSX)
255 char *filename = getCustomImageFilename(basename);
256 SDL_Surface *surface;
258 if (filename == NULL)
260 Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
265 if ((surface = IMG_Load(filename)) == NULL)
267 Error(ERR_WARN, "IMG_Load() failed: %s", SDL_GetError());
272 // set transparent color
273 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
274 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
276 SDL_SetWindowIcon(sdl_window, surface);
280 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
281 SDL_PixelFormat *format2)
283 return (format1->format == format2->format &&
284 format1->BitsPerPixel == format2->BitsPerPixel &&
285 format1->BytesPerPixel == format2->BytesPerPixel &&
286 format1->Rmask == format2->Rmask &&
287 format1->Gmask == format2->Gmask &&
288 format1->Bmask == format2->Bmask);
291 static Pixel SDLGetColorKey(SDL_Surface *surface)
295 if (SDL_GetColorKey(surface, &color_key) != 0)
301 static boolean SDLHasColorKey(SDL_Surface *surface)
303 return (SDLGetColorKey(surface) != -1);
306 static boolean SDLHasAlpha(SDL_Surface *surface)
308 SDL_BlendMode blend_mode;
310 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
313 return (blend_mode == SDL_BLENDMODE_BLEND);
316 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
318 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
320 SDL_SetSurfaceBlendMode(surface, blend_mode);
321 SDL_SetSurfaceAlphaMod(surface, alpha);
324 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
326 SDL_PixelFormat format;
327 SDL_Surface *new_surface;
332 if (backbuffer && backbuffer->surface)
334 format = *backbuffer->surface->format;
335 format.Amask = surface->format->Amask; // keep alpha channel
339 format = *surface->format;
342 new_surface = SDL_ConvertSurface(surface, &format, 0);
344 if (new_surface == NULL)
345 Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
350 boolean SDLSetNativeSurface(SDL_Surface **surface)
352 SDL_Surface *new_surface;
354 if (surface == NULL ||
356 backbuffer == NULL ||
357 backbuffer->surface == NULL)
360 // if pixel format already optimized for destination surface, do nothing
361 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
364 new_surface = SDLGetNativeSurface(*surface);
366 SDL_FreeSurface(*surface);
368 *surface = new_surface;
373 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
375 if (program.headless)
378 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
381 Error(ERR_EXIT, "SDL_CreateTextureFromSurface() failed: %s",
387 void SDLCreateBitmapTextures(Bitmap *bitmap)
393 SDL_DestroyTexture(bitmap->texture);
394 if (bitmap->texture_masked)
395 SDL_DestroyTexture(bitmap->texture_masked);
397 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
398 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
401 void SDLFreeBitmapTextures(Bitmap *bitmap)
407 SDL_DestroyTexture(bitmap->texture);
408 if (bitmap->texture_masked)
409 SDL_DestroyTexture(bitmap->texture_masked);
411 bitmap->texture = NULL;
412 bitmap->texture_masked = NULL;
415 void SDLInitVideoDisplay(void)
417 // initialize SDL video
418 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
419 Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
421 // set default SDL depth
422 video.default_depth = 32; // (how to determine video depth in SDL2?)
424 // Code used with SDL 1.2:
425 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
428 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
430 if (program.headless)
433 video.window_scaling_percent = setup.window_scaling_percent;
434 video.window_scaling_quality = setup.window_scaling_quality;
436 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
438 // SDL 2.0: support for (desktop) fullscreen mode available
439 video.fullscreen_available = TRUE;
441 // open SDL video output device (window or fullscreen mode)
442 if (!SDLSetVideoMode(fullscreen))
443 Error(ERR_EXIT, "setting video mode failed");
445 // !!! SDL2 can only set the window icon if the window already exists !!!
447 SDLSetWindowIcon(program.icon_filename);
449 // set window and icon title
453 static void SDLInitVideoBuffer_DrawBuffer(void)
455 /* SDL cannot directly draw to the visible video framebuffer like X11,
456 but always uses a backbuffer, which is then blitted to the visible
457 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
458 visible video framebuffer with 'SDL_Flip', if the hardware supports
459 this). Therefore do not use an additional backbuffer for drawing, but
460 use a symbolic buffer (distinguishable from the SDL backbuffer) called
461 'window', which indicates that the SDL backbuffer should be updated to
462 the visible video framebuffer when attempting to blit to it.
464 For convenience, it seems to be a good idea to create this symbolic
465 buffer 'window' at the same size as the SDL backbuffer. Although it
466 should never be drawn to directly, it would do no harm nevertheless. */
468 // create additional (symbolic) buffer for double-buffering
469 ReCreateBitmap(&window, video.width, video.height);
471 // create dummy drawing buffer for headless mode, if needed
472 if (program.headless)
473 ReCreateBitmap(&backbuffer, video.width, video.height);
476 void SDLInitVideoBuffer(boolean fullscreen)
478 SDLInitVideoBuffer_VideoBuffer(fullscreen);
479 SDLInitVideoBuffer_DrawBuffer();
482 static boolean SDLCreateScreen(boolean fullscreen)
484 SDL_Surface *new_surface = NULL;
486 int surface_flags_window = SURFACE_FLAGS | SDL_WINDOW_RESIZABLE;
487 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
490 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
492 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
493 _without_ enabling 2D/3D acceleration and/or guest additions installed,
494 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
495 it will try to use accelerated graphics and apparently fails miserably) */
496 int renderer_flags = SDL_RENDERER_SOFTWARE;
499 SDLSetScreenSizeAndOffsets(video.width, video.height);
501 int width = video.width;
502 int height = video.height;
503 int screen_width = video.screen_width;
504 int screen_height = video.screen_height;
505 int surface_flags = (fullscreen ? surface_flags_fullscreen :
506 surface_flags_window);
508 // default window size is unscaled
509 video.window_width = screen_width;
510 video.window_height = screen_height;
512 // store if initial screen mode is fullscreen mode when changing screen size
513 video.fullscreen_initial = fullscreen;
515 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
517 video.window_width = window_scaling_factor * screen_width;
518 video.window_height = window_scaling_factor * screen_height;
520 if (sdl_texture_stream)
522 SDL_DestroyTexture(sdl_texture_stream);
523 sdl_texture_stream = NULL;
526 if (sdl_texture_target)
528 SDL_DestroyTexture(sdl_texture_target);
529 sdl_texture_target = NULL;
532 if (!(fullscreen && fullscreen_enabled))
536 SDL_DestroyRenderer(sdl_renderer);
542 SDL_DestroyWindow(sdl_window);
547 if (sdl_window == NULL)
548 sdl_window = SDL_CreateWindow(program.window_title,
549 SDL_WINDOWPOS_CENTERED,
550 SDL_WINDOWPOS_CENTERED,
555 if (sdl_window != NULL)
557 if (sdl_renderer == NULL)
558 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
560 if (sdl_renderer != NULL)
562 SDL_RenderSetLogicalSize(sdl_renderer, screen_width, screen_height);
563 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
564 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
566 SDLSetScreenVsyncMode(setup.vsync_mode);
568 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
569 SDL_PIXELFORMAT_ARGB8888,
570 SDL_TEXTUREACCESS_STREAMING,
573 if (SDL_RenderTargetSupported(sdl_renderer))
574 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
575 SDL_PIXELFORMAT_ARGB8888,
576 SDL_TEXTUREACCESS_TARGET,
579 if (sdl_texture_stream != NULL)
581 // use SDL default values for RGB masks and no alpha channel
582 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
584 if (new_surface == NULL)
585 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
589 Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
594 Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
599 Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
602 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
603 if (new_surface != NULL)
604 fullscreen_enabled = fullscreen;
606 if (backbuffer == NULL)
607 backbuffer = CreateBitmapStruct();
609 backbuffer->width = video.width;
610 backbuffer->height = video.height;
612 if (backbuffer->surface)
613 SDL_FreeSurface(backbuffer->surface);
615 backbuffer->surface = new_surface;
617 return (new_surface != NULL);
620 boolean SDLSetVideoMode(boolean fullscreen)
622 boolean success = FALSE;
626 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
628 // switch display to fullscreen mode, if available
629 success = SDLCreateScreen(TRUE);
633 // switching display to fullscreen mode failed -- do not try it again
634 video.fullscreen_available = FALSE;
638 video.fullscreen_enabled = TRUE;
642 if ((!fullscreen && video.fullscreen_enabled) || !success)
644 // switch display to window mode
645 success = SDLCreateScreen(FALSE);
649 // switching display to window mode failed -- should not happen
653 video.fullscreen_enabled = FALSE;
654 video.window_scaling_percent = setup.window_scaling_percent;
655 video.window_scaling_quality = setup.window_scaling_quality;
657 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
661 SDLRedrawWindow(); // map window
664 #if defined(PLATFORM_WIN32)
665 // experimental drag and drop code
667 SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
670 SDL_SysWMinfo wminfo;
672 boolean wminfo_success = FALSE;
674 SDL_VERSION(&wminfo.version);
677 wminfo_success = SDL_GetWindowWMInfo(sdl_window, &wminfo);
681 hwnd = wminfo.info.win.window;
683 DragAcceptFiles(hwnd, TRUE);
692 void SDLSetWindowTitle(void)
694 if (sdl_window == NULL)
697 SDL_SetWindowTitle(sdl_window, program.window_title);
700 void SDLSetWindowScaling(int window_scaling_percent)
702 if (sdl_window == NULL)
705 float window_scaling_factor = (float)window_scaling_percent / 100;
706 int new_window_width = (int)(window_scaling_factor * video.screen_width);
707 int new_window_height = (int)(window_scaling_factor * video.screen_height);
709 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
711 video.window_scaling_percent = window_scaling_percent;
712 video.window_width = new_window_width;
713 video.window_height = new_window_height;
718 void SDLSetWindowScalingQuality(char *window_scaling_quality)
720 SDL_Texture *new_texture;
722 if (sdl_texture_stream == NULL)
725 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
727 new_texture = SDL_CreateTexture(sdl_renderer,
728 SDL_PIXELFORMAT_ARGB8888,
729 SDL_TEXTUREACCESS_STREAMING,
730 video.width, video.height);
732 if (new_texture != NULL)
734 SDL_DestroyTexture(sdl_texture_stream);
736 sdl_texture_stream = new_texture;
739 if (SDL_RenderTargetSupported(sdl_renderer))
740 new_texture = SDL_CreateTexture(sdl_renderer,
741 SDL_PIXELFORMAT_ARGB8888,
742 SDL_TEXTUREACCESS_TARGET,
743 video.width, video.height);
747 if (new_texture != NULL)
749 SDL_DestroyTexture(sdl_texture_target);
751 sdl_texture_target = new_texture;
756 video.window_scaling_quality = window_scaling_quality;
759 void SDLSetWindowFullscreen(boolean fullscreen)
761 if (sdl_window == NULL)
764 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
766 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
767 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
769 // if screen size was changed in fullscreen mode, correct desktop window size
770 if (!fullscreen && video.fullscreen_initial)
772 SDLSetWindowScaling(setup.window_scaling_percent);
773 SDL_SetWindowPosition(sdl_window,
774 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
776 video.fullscreen_initial = FALSE;
780 void SDLSetDisplaySize(void)
782 SDL_Rect display_bounds;
784 SDL_GetDisplayBounds(0, &display_bounds);
786 video.display_width = display_bounds.w;
787 video.display_height = display_bounds.h;
790 Error(ERR_DEBUG, "SDL real screen size: %d x %d",
791 video.display_width, video.display_height);
795 void SDLSetScreenSizeAndOffsets(int width, int height)
797 // set default video screen size and offsets
798 video.screen_width = width;
799 video.screen_height = height;
800 video.screen_xoffset = 0;
801 video.screen_yoffset = 0;
803 #if defined(USE_COMPLETE_DISPLAY)
804 float ratio_video = (float) width / height;
805 float ratio_display = (float) video.display_width / video.display_height;
807 if (ratio_video != ratio_display)
809 // adjust drawable screen size to cover the whole device display
811 if (ratio_video < ratio_display)
812 video.screen_width *= ratio_display / ratio_video;
814 video.screen_height *= ratio_video / ratio_display;
816 video.screen_xoffset = (video.screen_width - width) / 2;
817 video.screen_yoffset = (video.screen_height - height) / 2;
820 Error(ERR_DEBUG, "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
822 video.screen_width, video.screen_height,
823 ratio_video, ratio_display);
829 void SDLSetScreenSizeForRenderer(int width, int height)
831 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
834 void SDLSetScreenProperties(void)
836 SDLSetScreenSizeAndOffsets(video.width, video.height);
837 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
840 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
842 video.screen_rendering_mode =
843 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
844 SPECIAL_RENDERING_BITMAP :
845 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
846 SPECIAL_RENDERING_TARGET:
847 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
848 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
851 void SDLSetScreenVsyncMode(char *vsync_mode)
854 (strEqual(vsync_mode, STR_VSYNC_MODE_NORMAL) ? VSYNC_MODE_NORMAL :
855 strEqual(vsync_mode, STR_VSYNC_MODE_ADAPTIVE) ? VSYNC_MODE_ADAPTIVE :
857 int result = SDL_GL_SetSwapInterval(interval);
859 // if adaptive vsync requested, but not supported, retry with normal vsync
860 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
861 SDL_GL_SetSwapInterval(VSYNC_MODE_NORMAL);
864 void SDLRedrawWindow(void)
866 UpdateScreen_WithoutFrameDelay(NULL);
869 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
872 if (program.headless)
875 SDL_Surface *surface =
876 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
879 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
881 SDLSetNativeSurface(&surface);
883 bitmap->surface = surface;
886 void SDLFreeBitmapPointers(Bitmap *bitmap)
889 SDL_FreeSurface(bitmap->surface);
890 if (bitmap->surface_masked)
891 SDL_FreeSurface(bitmap->surface_masked);
893 bitmap->surface = NULL;
894 bitmap->surface_masked = NULL;
897 SDL_DestroyTexture(bitmap->texture);
898 if (bitmap->texture_masked)
899 SDL_DestroyTexture(bitmap->texture_masked);
901 bitmap->texture = NULL;
902 bitmap->texture_masked = NULL;
905 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
906 int src_x, int src_y, int width, int height,
907 int dst_x, int dst_y, int mask_mode)
909 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
910 SDL_Rect src_rect, dst_rect;
922 // if (src_bitmap != backbuffer || dst_bitmap != window)
923 if (!(src_bitmap == backbuffer && dst_bitmap == window))
924 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
925 src_bitmap->surface_masked : src_bitmap->surface),
926 &src_rect, real_dst_bitmap->surface, &dst_rect);
928 if (dst_bitmap == window)
929 UpdateScreen_WithFrameDelay(&dst_rect);
932 void SDLBlitTexture(Bitmap *bitmap,
933 int src_x, int src_y, int width, int height,
934 int dst_x, int dst_y, int mask_mode)
936 SDL_Texture *texture;
941 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
956 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
959 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
962 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
970 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
972 if (dst_bitmap == window)
973 UpdateScreen_WithFrameDelay(&rect);
976 void PrepareFadeBitmap(int draw_target)
978 Bitmap *fade_bitmap =
979 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
980 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
982 if (fade_bitmap == NULL)
985 // copy backbuffer to fading buffer
986 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
988 // add border and animations to fading buffer
989 FinalizeScreen(draw_target);
992 void SDLFadeRectangle(int x, int y, int width, int height,
993 int fade_mode, int fade_delay, int post_delay,
994 void (*draw_border_function)(void))
996 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
997 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
998 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
999 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1000 SDL_Surface *surface_screen = backbuffer->surface;
1001 SDL_Rect src_rect, dst_rect;
1003 int src_x = x, src_y = y;
1004 int dst_x = x, dst_y = y;
1005 unsigned int time_last, time_current;
1007 // store function for drawing global masked border
1008 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1010 // deactivate drawing of global border while fading, if needed
1011 if (draw_border_function == NULL)
1012 gfx.draw_global_border_function = NULL;
1017 src_rect.h = height;
1021 dst_rect.w = width; // (ignored)
1022 dst_rect.h = height; // (ignored)
1024 dst_rect2 = dst_rect;
1026 // before fading in, store backbuffer (without animation graphics)
1027 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1028 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1030 // copy source and target surfaces to temporary surfaces for fading
1031 if (fade_mode & FADE_TYPE_TRANSFORM)
1033 // (source and target fading buffer already prepared)
1035 else if (fade_mode & FADE_TYPE_FADE_IN)
1037 // (target fading buffer already prepared)
1038 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1040 else // FADE_TYPE_FADE_OUT
1042 // (source fading buffer already prepared)
1043 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1046 time_current = SDL_GetTicks();
1048 if (fade_mode == FADE_MODE_MELT)
1050 boolean done = FALSE;
1051 int melt_pixels = 2;
1052 int melt_columns = width / melt_pixels;
1053 int ypos[melt_columns];
1054 int max_steps = height / 8 + 32;
1059 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1061 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1063 ypos[0] = -GetSimpleRandom(16);
1065 for (i = 1 ; i < melt_columns; i++)
1067 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1069 ypos[i] = ypos[i - 1] + r;
1082 time_last = time_current;
1083 time_current = SDL_GetTicks();
1084 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1085 steps_final = MIN(MAX(0, steps), max_steps);
1089 done = (steps_done >= steps_final);
1091 for (i = 0 ; i < melt_columns; i++)
1099 else if (ypos[i] < height)
1104 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1106 if (ypos[i] + dy >= height)
1107 dy = height - ypos[i];
1109 // copy part of (appearing) target surface to upper area
1110 src_rect.x = src_x + i * melt_pixels;
1111 // src_rect.y = src_y + ypos[i];
1113 src_rect.w = melt_pixels;
1115 src_rect.h = ypos[i] + dy;
1117 dst_rect.x = dst_x + i * melt_pixels;
1118 // dst_rect.y = dst_y + ypos[i];
1121 if (steps_done >= steps_final)
1122 SDL_BlitSurface(surface_target, &src_rect,
1123 surface_screen, &dst_rect);
1127 // copy part of (disappearing) source surface to lower area
1128 src_rect.x = src_x + i * melt_pixels;
1130 src_rect.w = melt_pixels;
1131 src_rect.h = height - ypos[i];
1133 dst_rect.x = dst_x + i * melt_pixels;
1134 dst_rect.y = dst_y + ypos[i];
1136 if (steps_done >= steps_final)
1137 SDL_BlitSurface(surface_source, &src_rect,
1138 surface_screen, &dst_rect);
1144 src_rect.x = src_x + i * melt_pixels;
1146 src_rect.w = melt_pixels;
1147 src_rect.h = height;
1149 dst_rect.x = dst_x + i * melt_pixels;
1152 if (steps_done >= steps_final)
1153 SDL_BlitSurface(surface_target, &src_rect,
1154 surface_screen, &dst_rect);
1158 if (steps_done >= steps_final)
1160 if (draw_border_function != NULL)
1161 draw_border_function();
1163 UpdateScreen_WithFrameDelay(&dst_rect2);
1167 else if (fade_mode == FADE_MODE_CURTAIN)
1171 int xx_size = width / 2;
1173 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1175 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1177 for (xx = 0; xx < xx_size;)
1179 time_last = time_current;
1180 time_current = SDL_GetTicks();
1181 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1182 xx_final = MIN(MAX(0, xx), xx_size);
1187 src_rect.h = height;
1192 // draw new (target) image to screen buffer
1193 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1195 if (xx_final < xx_size)
1197 src_rect.w = xx_size - xx_final;
1198 src_rect.h = height;
1200 // draw old (source) image to screen buffer (left side)
1202 src_rect.x = src_x + xx_final;
1205 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1207 // draw old (source) image to screen buffer (right side)
1209 src_rect.x = src_x + xx_size;
1210 dst_rect.x = dst_x + xx_size + xx_final;
1212 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1215 if (draw_border_function != NULL)
1216 draw_border_function();
1218 // only update the region of the screen that is affected from fading
1219 UpdateScreen_WithFrameDelay(&dst_rect2);
1222 else // fading in, fading out or cross-fading
1227 for (alpha = 0.0; alpha < 255.0;)
1229 time_last = time_current;
1230 time_current = SDL_GetTicks();
1231 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1232 alpha_final = MIN(MAX(0, alpha), 255);
1234 // draw existing (source) image to screen buffer
1235 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1237 // draw new (target) image to screen buffer using alpha blending
1238 SDLSetAlpha(surface_target, TRUE, alpha_final);
1239 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1241 if (draw_border_function != NULL)
1242 draw_border_function();
1244 // only update the region of the screen that is affected from fading
1245 UpdateScreen_WithFrameDelay(&dst_rect);
1250 Delay_WithScreenUpdates(post_delay);
1252 // restore function for drawing global masked border
1253 gfx.draw_global_border_function = draw_global_border_function;
1255 // after fading in, restore backbuffer (without animation graphics)
1256 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1257 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1260 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1261 int to_x, int to_y, Uint32 color)
1263 SDL_Surface *surface = dst_bitmap->surface;
1267 swap_numbers(&from_x, &to_x);
1270 swap_numbers(&from_y, &to_y);
1274 rect.w = (to_x - from_x + 1);
1275 rect.h = (to_y - from_y + 1);
1277 SDL_FillRect(surface, &rect, color);
1280 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1281 int to_x, int to_y, Uint32 color)
1283 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1286 #if ENABLE_UNUSED_CODE
1287 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1288 int num_points, Uint32 color)
1293 for (i = 0; i < num_points - 1; i++)
1295 for (x = 0; x < line_width; x++)
1297 for (y = 0; y < line_width; y++)
1299 int dx = x - line_width / 2;
1300 int dy = y - line_width / 2;
1302 if ((x == 0 && y == 0) ||
1303 (x == 0 && y == line_width - 1) ||
1304 (x == line_width - 1 && y == 0) ||
1305 (x == line_width - 1 && y == line_width - 1))
1308 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1309 points[i+1].x + dx, points[i+1].y + dy, color);
1316 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1318 SDL_Surface *surface = src_bitmap->surface;
1320 switch (surface->format->BytesPerPixel)
1322 case 1: // assuming 8-bpp
1324 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1328 case 2: // probably 15-bpp or 16-bpp
1330 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1334 case 3: // slow 24-bpp mode; usually not used
1337 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1341 shift = surface->format->Rshift;
1342 color |= *(pix + shift / 8) >> shift;
1343 shift = surface->format->Gshift;
1344 color |= *(pix + shift / 8) >> shift;
1345 shift = surface->format->Bshift;
1346 color |= *(pix + shift / 8) >> shift;
1352 case 4: // probably 32-bpp
1354 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1363 // ============================================================================
1364 // The following functions were taken from the SGE library
1365 // (SDL Graphics Extension Library) by Anders Lindström
1366 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1367 // ============================================================================
1369 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1371 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1373 switch (surface->format->BytesPerPixel)
1378 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1384 // Probably 15-bpp or 16-bpp
1385 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1391 // Slow 24-bpp mode, usually not used
1395 // Gack - slow, but endian correct
1396 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1397 shift = surface->format->Rshift;
1398 *(pix+shift/8) = color>>shift;
1399 shift = surface->format->Gshift;
1400 *(pix+shift/8) = color>>shift;
1401 shift = surface->format->Bshift;
1402 *(pix+shift/8) = color>>shift;
1409 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1417 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1418 Uint8 R, Uint8 G, Uint8 B)
1420 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1423 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1425 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1428 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1430 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1433 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1438 // Gack - slow, but endian correct
1439 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1440 shift = surface->format->Rshift;
1441 *(pix+shift/8) = color>>shift;
1442 shift = surface->format->Gshift;
1443 *(pix+shift/8) = color>>shift;
1444 shift = surface->format->Bshift;
1445 *(pix+shift/8) = color>>shift;
1448 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1450 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1453 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1455 switch (dest->format->BytesPerPixel)
1458 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1462 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1466 _PutPixel24(dest,x,y,color);
1470 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1476 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1478 if (SDL_MUSTLOCK(surface))
1480 if (SDL_LockSurface(surface) < 0)
1486 _PutPixel(surface, x, y, color);
1488 if (SDL_MUSTLOCK(surface))
1490 SDL_UnlockSurface(surface);
1495 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1496 Uint8 r, Uint8 g, Uint8 b)
1498 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1501 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1503 if (y >= 0 && y <= dest->h - 1)
1505 switch (dest->format->BytesPerPixel)
1508 return y*dest->pitch;
1512 return y*dest->pitch/2;
1516 return y*dest->pitch;
1520 return y*dest->pitch/4;
1528 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1531 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1533 switch (surface->format->BytesPerPixel)
1538 *((Uint8 *)surface->pixels + ypitch + x) = color;
1544 // Probably 15-bpp or 16-bpp
1545 *((Uint16 *)surface->pixels + ypitch + x) = color;
1551 // Slow 24-bpp mode, usually not used
1555 // Gack - slow, but endian correct
1556 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1557 shift = surface->format->Rshift;
1558 *(pix+shift/8) = color>>shift;
1559 shift = surface->format->Gshift;
1560 *(pix+shift/8) = color>>shift;
1561 shift = surface->format->Bshift;
1562 *(pix+shift/8) = color>>shift;
1569 *((Uint32 *)surface->pixels + ypitch + x) = color;
1576 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1581 if (SDL_MUSTLOCK(Surface))
1583 if (SDL_LockSurface(Surface) < 0)
1597 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1601 if (x2 > Surface->w - 1)
1602 x2 = Surface->w - 1;
1609 SDL_FillRect(Surface, &l, Color);
1611 if (SDL_MUSTLOCK(Surface))
1613 SDL_UnlockSurface(Surface);
1617 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1618 Uint8 R, Uint8 G, Uint8 B)
1620 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1623 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1636 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1640 if (x2 > Surface->w - 1)
1641 x2 = Surface->w - 1;
1648 SDL_FillRect(Surface, &l, Color);
1651 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1656 if (SDL_MUSTLOCK(Surface))
1658 if (SDL_LockSurface(Surface) < 0)
1672 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1676 if (y2 > Surface->h - 1)
1677 y2 = Surface->h - 1;
1684 SDL_FillRect(Surface, &l, Color);
1686 if (SDL_MUSTLOCK(Surface))
1688 SDL_UnlockSurface(Surface);
1692 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1693 Uint8 R, Uint8 G, Uint8 B)
1695 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1698 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1711 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1715 if (y2 > Surface->h - 1)
1716 y2 = Surface->h - 1;
1723 SDL_FillRect(Surface, &l, Color);
1727 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1728 Sint16 x2, Sint16 y2, Uint32 Color,
1729 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1732 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1737 sdx = (dx < 0) ? -1 : 1;
1738 sdy = (dy < 0) ? -1 : 1;
1750 for (x = 0; x < dx; x++)
1752 Callback(Surface, px, py, Color);
1766 for (y = 0; y < dy; y++)
1768 Callback(Surface, px, py, Color);
1783 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1784 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1785 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1788 sge_DoLine(Surface, X1, Y1, X2, Y2,
1789 SDL_MapRGB(Surface->format, R, G, B), Callback);
1793 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1796 if (SDL_MUSTLOCK(Surface))
1798 if (SDL_LockSurface(Surface) < 0)
1803 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1805 // unlock the display
1806 if (SDL_MUSTLOCK(Surface))
1808 SDL_UnlockSurface(Surface);
1813 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1814 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1816 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1820 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1822 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1826 // ----------------------------------------------------------------------------
1827 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1828 // ----------------------------------------------------------------------------
1830 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1831 int width, int height, Uint32 color)
1835 for (y = src_y; y < src_y + height; y++)
1837 for (x = src_x; x < src_x + width; x++)
1839 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1841 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1846 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1847 int src_x, int src_y, int width, int height,
1848 int dst_x, int dst_y)
1852 for (y = 0; y < height; y++)
1854 for (x = 0; x < width; x++)
1856 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1858 if (pixel != BLACK_PIXEL)
1859 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1865 // ============================================================================
1866 // The following functions were taken from the SDL_gfx library version 2.0.3
1867 // (Rotozoomer) by Andreas Schiffler
1868 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1869 // ============================================================================
1871 // ----------------------------------------------------------------------------
1874 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1875 // ----------------------------------------------------------------------------
1885 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1888 tColorRGBA *sp, *csp, *dp;
1892 sp = csp = (tColorRGBA *) src->pixels;
1893 dp = (tColorRGBA *) dst->pixels;
1894 dgap = dst->pitch - dst->w * 4;
1896 for (y = 0; y < dst->h; y++)
1900 for (x = 0; x < dst->w; x++)
1902 tColorRGBA *sp0 = sp;
1903 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1904 tColorRGBA *sp00 = &sp0[0];
1905 tColorRGBA *sp01 = &sp0[1];
1906 tColorRGBA *sp10 = &sp1[0];
1907 tColorRGBA *sp11 = &sp1[1];
1910 // create new color pixel from all four source color pixels
1911 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1912 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1913 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1914 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1919 // advance source pointers
1922 // advance destination pointer
1926 // advance source pointer
1927 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1929 // advance destination pointers
1930 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1936 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1938 int x, y, *sax, *say, *csax, *csay;
1940 tColorRGBA *sp, *csp, *csp0, *dp;
1943 // use specialized zoom function when scaling down to exactly half size
1944 if (src->w == 2 * dst->w &&
1945 src->h == 2 * dst->h)
1946 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1949 sx = (float) src->w / (float) dst->w;
1950 sy = (float) src->h / (float) dst->h;
1952 // allocate memory for row increments
1953 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1954 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1956 // precalculate row increments
1957 for (x = 0; x <= dst->w; x++)
1958 *csax++ = (int)(sx * x);
1960 for (y = 0; y <= dst->h; y++)
1961 *csay++ = (int)(sy * y);
1964 sp = csp = csp0 = (tColorRGBA *) src->pixels;
1965 dp = (tColorRGBA *) dst->pixels;
1966 dgap = dst->pitch - dst->w * 4;
1969 for (y = 0; y < dst->h; y++)
1974 for (x = 0; x < dst->w; x++)
1979 // advance source pointers
1983 // advance destination pointer
1987 // advance source pointer
1989 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
1991 // advance destination pointers
1992 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2001 // ----------------------------------------------------------------------------
2004 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2005 // ----------------------------------------------------------------------------
2007 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2009 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2010 Uint8 *sp, *dp, *csp;
2014 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2015 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2017 // allocate memory for row increments
2018 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2019 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2021 // precalculate row increments
2024 for (x = 0; x < dst->w; x++)
2027 *csax = (csx >> 16);
2034 for (y = 0; y < dst->h; y++)
2037 *csay = (csy >> 16);
2044 for (x = 0; x < dst->w; x++)
2052 for (y = 0; y < dst->h; y++)
2059 sp = csp = (Uint8 *) src->pixels;
2060 dp = (Uint8 *) dst->pixels;
2061 dgap = dst->pitch - dst->w;
2065 for (y = 0; y < dst->h; y++)
2069 for (x = 0; x < dst->w; x++)
2074 // advance source pointers
2078 // advance destination pointer
2082 // advance source pointer (for row)
2083 csp += ((*csay) * src->pitch);
2086 // advance destination pointers
2096 // ----------------------------------------------------------------------------
2099 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2100 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2101 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2102 // into a 32bit RGBA format on the fly.
2103 // ----------------------------------------------------------------------------
2105 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2107 SDL_Surface *zoom_src = NULL;
2108 SDL_Surface *zoom_dst = NULL;
2109 boolean is_converted = FALSE;
2116 // determine if source surface is 32 bit or 8 bit
2117 is_32bit = (src->format->BitsPerPixel == 32);
2119 if (is_32bit || src->format->BitsPerPixel == 8)
2121 // use source surface 'as is'
2126 // new source surface is 32 bit with a defined RGB ordering
2127 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2128 0x000000ff, 0x0000ff00, 0x00ff0000,
2129 (src->format->Amask ? 0xff000000 : 0));
2130 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2132 is_converted = TRUE;
2135 // allocate surface to completely contain the zoomed surface
2138 // target surface is 32 bit with source RGBA/ABGR ordering
2139 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2140 zoom_src->format->Rmask,
2141 zoom_src->format->Gmask,
2142 zoom_src->format->Bmask,
2143 zoom_src->format->Amask);
2147 // target surface is 8 bit
2148 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2152 // lock source surface
2153 SDL_LockSurface(zoom_src);
2155 // check which kind of surface we have
2158 // call the 32 bit transformation routine to do the zooming
2159 zoomSurfaceRGBA(zoom_src, zoom_dst);
2164 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2165 zoom_dst->format->palette->colors[i] =
2166 zoom_src->format->palette->colors[i];
2167 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2169 // call the 8 bit transformation routine to do the zooming
2170 zoomSurfaceY(zoom_src, zoom_dst);
2173 // unlock source surface
2174 SDL_UnlockSurface(zoom_src);
2176 // free temporary surface
2178 SDL_FreeSurface(zoom_src);
2180 // return destination surface
2184 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2186 SDL_Surface *new_surface;
2188 if (surface == NULL)
2191 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2192 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2194 // remove alpha channel from native non-transparent surface, if defined
2195 SDLSetAlpha(new_surface, FALSE, 0);
2197 // remove transparent color from native non-transparent surface, if defined
2198 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2203 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2205 Bitmap *dst_bitmap = CreateBitmapStruct();
2206 SDL_Surface *src_surface = src_bitmap->surface_masked;
2207 SDL_Surface *dst_surface;
2209 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2210 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2212 dst_bitmap->width = dst_width;
2213 dst_bitmap->height = dst_height;
2215 // create zoomed temporary surface from source surface
2216 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2218 // create native format destination surface from zoomed temporary surface
2219 SDLSetNativeSurface(&dst_surface);
2221 // set color key for zoomed surface from source surface, if defined
2222 if (SDLHasColorKey(src_surface))
2223 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2224 SDLGetColorKey(src_surface));
2226 // create native non-transparent surface for opaque blitting
2227 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2229 // set native transparent surface for masked blitting
2230 dst_bitmap->surface_masked = dst_surface;
2236 // ============================================================================
2237 // load image to bitmap
2238 // ============================================================================
2240 Bitmap *SDLLoadImage(char *filename)
2242 Bitmap *new_bitmap = CreateBitmapStruct();
2243 SDL_Surface *sdl_image_tmp;
2245 if (program.headless)
2247 // prevent sanity check warnings at later stage
2248 new_bitmap->width = new_bitmap->height = 1;
2253 print_timestamp_init("SDLLoadImage");
2255 print_timestamp_time(getBaseNamePtr(filename));
2257 // load image to temporary surface
2258 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2259 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
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 SDLHandleWindowManagerEvent(Event *event)
2394 #if defined(PLATFORM_WIN32)
2395 // experimental drag and drop code
2397 SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
2398 SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
2400 if (syswmmsg->msg.win.msg == WM_DROPFILES)
2402 HDROP hdrop = (HDROP)syswmmsg->msg.win.wParam;
2405 printf("::: SDL_SYSWMEVENT:\n");
2407 num_files = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
2409 for (i = 0; i < num_files; i++)
2411 int buffer_len = DragQueryFile(hdrop, i, NULL, 0);
2412 char buffer[buffer_len + 1];
2414 DragQueryFile(hdrop, i, buffer, buffer_len + 1);
2416 printf("::: - '%s'\n", buffer);
2419 DragFinish((HDROP)syswmmsg->msg.win.wParam);
2426 // ============================================================================
2427 // joystick functions
2428 // ============================================================================
2430 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2431 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2432 static int sdl_js_axis[MAX_PLAYERS][2];
2433 static int sdl_js_button[MAX_PLAYERS][2];
2434 static boolean sdl_is_controller[MAX_PLAYERS];
2436 void SDLClearJoystickState(void)
2440 for (i = 0; i < MAX_PLAYERS; i++)
2442 for (j = 0; j < 2; j++)
2444 sdl_js_axis_raw[i][j] = -1;
2445 sdl_js_axis[i][j] = 0;
2446 sdl_js_button[i][j] = 0;
2451 boolean SDLOpenJoystick(int nr)
2453 if (nr < 0 || nr >= MAX_PLAYERS)
2456 sdl_is_controller[nr] = SDL_IsGameController(nr);
2459 Error(ERR_DEBUG, "opening joystick %d (%s)",
2460 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2463 if (sdl_is_controller[nr])
2464 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2466 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2468 return (sdl_joystick[nr] != NULL);
2471 void SDLCloseJoystick(int nr)
2473 if (nr < 0 || nr >= MAX_PLAYERS)
2477 Error(ERR_DEBUG, "closing joystick %d", nr);
2480 if (sdl_is_controller[nr])
2481 SDL_GameControllerClose(sdl_joystick[nr]);
2483 SDL_JoystickClose(sdl_joystick[nr]);
2485 sdl_joystick[nr] = NULL;
2488 boolean SDLCheckJoystickOpened(int nr)
2490 if (nr < 0 || nr >= MAX_PLAYERS)
2493 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2496 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2498 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2499 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2500 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2501 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2503 if (nr < 0 || nr >= MAX_PLAYERS)
2509 // prevent (slightly jittering, but centered) axis A from resetting axis B
2510 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2511 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2514 sdl_js_axis[nr][axis_id] = axis_value;
2515 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2518 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2520 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2521 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2522 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2523 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2524 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2525 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2526 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2527 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2530 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2531 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2532 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2533 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2534 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2535 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2536 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2537 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2539 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2540 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2541 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2542 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2543 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2545 if (nr < 0 || nr >= MAX_PLAYERS)
2548 if (button_id == -1)
2551 sdl_js_button[nr][button_id] = button_state;
2554 void HandleJoystickEvent(Event *event)
2558 case SDL_CONTROLLERDEVICEADDED:
2560 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2561 event->cdevice.which);
2566 case SDL_CONTROLLERDEVICEREMOVED:
2568 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2569 event->cdevice.which);
2574 case SDL_CONTROLLERAXISMOTION:
2576 Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2577 event->caxis.which, event->caxis.axis, event->caxis.value);
2579 setJoystickAxis(event->caxis.which,
2581 event->caxis.value);
2584 case SDL_CONTROLLERBUTTONDOWN:
2586 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2587 event->cbutton.which, event->cbutton.button);
2589 setJoystickButton(event->cbutton.which,
2590 event->cbutton.button,
2594 case SDL_CONTROLLERBUTTONUP:
2596 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2597 event->cbutton.which, event->cbutton.button);
2599 setJoystickButton(event->cbutton.which,
2600 event->cbutton.button,
2604 case SDL_JOYAXISMOTION:
2605 if (sdl_is_controller[event->jaxis.which])
2609 Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2610 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2612 if (event->jaxis.axis < 4)
2613 setJoystickAxis(event->jaxis.which,
2615 event->jaxis.value);
2618 case SDL_JOYBUTTONDOWN:
2619 if (sdl_is_controller[event->jaxis.which])
2623 Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2624 event->jbutton.which, event->jbutton.button);
2626 if (event->jbutton.button < 4)
2627 setJoystickButton(event->jbutton.which,
2628 event->jbutton.button,
2632 case SDL_JOYBUTTONUP:
2633 if (sdl_is_controller[event->jaxis.which])
2637 Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2638 event->jbutton.which, event->jbutton.button);
2640 if (event->jbutton.button < 4)
2641 setJoystickButton(event->jbutton.which,
2642 event->jbutton.button,
2651 void SDLInitJoysticks(void)
2653 static boolean sdl_joystick_subsystem_initialized = FALSE;
2654 boolean print_warning = !sdl_joystick_subsystem_initialized;
2655 char *mappings_file_base = getPath2(options.conf_directory,
2656 GAMECONTROLLER_BASENAME);
2657 char *mappings_file_user = getPath2(getUserGameDataDir(),
2658 GAMECONTROLLER_BASENAME);
2662 if (!sdl_joystick_subsystem_initialized)
2664 sdl_joystick_subsystem_initialized = TRUE;
2666 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2668 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2670 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2674 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2676 // the included game controller base mappings should always be found
2677 if (num_mappings == -1)
2678 Error(ERR_WARN, "no game controller base mappings found");
2681 Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2684 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2687 // the personal game controller user mappings may or may not be found
2688 if (num_mappings == -1)
2689 Error(ERR_WARN, "no game controller user mappings found");
2691 Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2693 Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2696 checked_free(mappings_file_base);
2697 checked_free(mappings_file_user);
2700 for (i = 0; i < SDL_NumJoysticks(); i++)
2702 const char *name, *type;
2704 if (SDL_IsGameController(i))
2706 name = SDL_GameControllerNameForIndex(i);
2707 type = "game controller";
2711 name = SDL_JoystickNameForIndex(i);
2715 Error(ERR_INFO, "- joystick %d (%s): '%s'",
2716 i, type, (name ? name : "(Unknown)"));
2721 // assign joysticks from configured to connected joystick for all players
2722 for (i = 0; i < MAX_PLAYERS; i++)
2724 // get configured joystick for this player
2725 char *device_name = setup.input[i].joy.device_name;
2726 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2728 if (joystick_nr >= SDL_NumJoysticks())
2730 if (setup.input[i].use_joystick && print_warning)
2731 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2736 // store configured joystick number for each player
2737 joystick.nr[i] = joystick_nr;
2740 // now open all connected joysticks (regardless if configured or not)
2741 for (i = 0; i < SDL_NumJoysticks(); i++)
2743 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2744 if (SDLCheckJoystickOpened(i))
2745 SDLCloseJoystick(i);
2747 if (SDLOpenJoystick(i))
2748 joystick.status = JOYSTICK_ACTIVATED;
2749 else if (print_warning)
2750 Error(ERR_WARN, "cannot open joystick %d", i);
2753 SDLClearJoystickState();
2756 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2758 if (nr < 0 || nr >= MAX_PLAYERS)
2762 *x = sdl_js_axis[nr][0];
2764 *y = sdl_js_axis[nr][1];
2767 *b1 = sdl_js_button[nr][0];
2769 *b2 = sdl_js_button[nr][1];
2775 // ============================================================================
2776 // touch input overlay functions
2777 // ============================================================================
2779 #if defined(USE_TOUCH_INPUT_OVERLAY)
2780 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2783 int grid_xsize = overlay.grid_xsize;
2784 int grid_ysize = overlay.grid_ysize;
2787 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2788 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2790 for (x = 0; x < grid_xsize; x++)
2792 rect.x = (x + 0) * video.screen_width / grid_xsize;
2793 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2795 for (y = 0; y < grid_ysize; y++)
2797 rect.y = (y + 0) * video.screen_height / grid_ysize;
2798 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2800 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2801 SDL_RenderDrawRect(sdl_renderer, &rect);
2805 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2808 static void RenderFillRectangle(int x, int y, int width, int height)
2810 SDL_Rect rect = { x, y, width, height };
2812 SDL_RenderFillRect(sdl_renderer, &rect);
2815 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2817 static int alpha_direction = 0;
2818 static int alpha_highlight = 0;
2819 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2820 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2822 int grid_xsize = overlay.grid_xsize;
2823 int grid_ysize = overlay.grid_ysize;
2826 if (alpha == alpha_max)
2828 if (alpha_direction < 0)
2830 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2832 if (alpha_highlight == 0)
2833 alpha_direction = 1;
2837 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2839 if (alpha_highlight == alpha_max)
2840 alpha_direction = -1;
2845 alpha_direction = 1;
2846 alpha_highlight = alpha;
2849 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2851 for (x = 0; x < grid_xsize; x++)
2853 for (y = 0; y < grid_ysize; y++)
2855 int grid_button = overlay.grid_button[x][y];
2856 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2857 int alpha_draw = alpha;
2858 int outline_border = MV_NONE;
2859 int border_size = 2;
2860 boolean draw_outlined = setup.touch.draw_outlined;
2861 boolean draw_pressed = setup.touch.draw_pressed;
2863 if (grid_button == CHAR_GRID_BUTTON_NONE)
2866 if (grid_button == overlay.grid_button_highlight)
2867 alpha_draw = alpha_highlight;
2869 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2872 draw_outlined = FALSE;
2874 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2877 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2879 rect.x = (x + 0) * video.screen_width / grid_xsize;
2880 rect.y = (y + 0) * video.screen_height / grid_ysize;
2881 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2882 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2884 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2886 rect.x += border_size;
2887 rect.w -= border_size;
2889 outline_border |= MV_LEFT;
2892 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2894 rect.w -= border_size;
2896 outline_border |= MV_RIGHT;
2899 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2901 rect.y += border_size;
2902 rect.h -= border_size;
2904 outline_border |= MV_UP;
2907 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2909 rect.h -= border_size;
2911 outline_border |= MV_DOWN;
2916 int rect_x = rect.x +
2917 (outline_border & MV_LEFT ? border_size : 0);
2918 int rect_w = rect.w -
2919 (outline_border & MV_LEFT ? border_size : 0) -
2920 (outline_border & MV_RIGHT ? border_size : 0);
2922 if (outline_border & MV_LEFT)
2923 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2925 if (outline_border & MV_RIGHT)
2926 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2927 border_size, rect.h);
2929 if (outline_border & MV_UP)
2930 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2932 if (outline_border & MV_DOWN)
2933 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
2934 rect_w, border_size);
2938 SDL_RenderFillRect(sdl_renderer, &rect);
2943 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2946 static void DrawTouchInputOverlay(void)
2948 static SDL_Texture *texture = NULL;
2949 static boolean initialized = FALSE;
2950 static boolean deactivated = TRUE;
2951 static boolean show_grid = FALSE;
2952 static int width = 0, height = 0;
2953 static int alpha_last = -1;
2954 static int alpha = 0;
2955 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2956 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2957 boolean active = (overlay.enabled && overlay.active);
2959 if (!active && deactivated)
2964 if (alpha < alpha_max)
2965 alpha = MIN(alpha + alpha_step, alpha_max);
2967 deactivated = FALSE;
2971 alpha = MAX(0, alpha - alpha_step);
2977 if (overlay.show_grid)
2979 else if (deactivated)
2983 DrawTouchInputOverlay_ShowGrid(alpha);
2985 DrawTouchInputOverlay_ShowGridButtons(alpha);
2990 // !!! VIRTUAL BUTTONS FROM IMAGE FILE NOT USED ANYMORE !!!
2994 char *basename = "overlay/VirtualButtons.png";
2995 char *filename = getCustomImageFilename(basename);
2997 if (filename == NULL)
2998 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
3000 SDL_Surface *surface;
3002 if ((surface = IMG_Load(filename)) == NULL)
3003 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
3006 height = surface->h;
3008 // set black pixel to transparent if no alpha channel / transparent color
3009 if (!SDLHasAlpha(surface) &&
3010 !SDLHasColorKey(surface))
3011 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
3012 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
3014 if ((texture = SDLCreateTextureFromSurface(surface)) == NULL)
3015 Error(ERR_EXIT, "SDLCreateTextureFromSurface() failed");
3017 SDL_FreeSurface(surface);
3019 SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
3024 if (alpha != alpha_last)
3025 SDL_SetTextureAlphaMod(texture, alpha);
3029 float ratio_overlay = (float) width / height;
3030 float ratio_screen = (float) video.screen_width / video.screen_height;
3031 int width_scaled, height_scaled;
3034 if (ratio_overlay > ratio_screen)
3036 width_scaled = video.screen_width;
3037 height_scaled = video.screen_height * ratio_screen / ratio_overlay;
3039 ypos = video.screen_height - height_scaled;
3043 width_scaled = video.screen_width * ratio_overlay / ratio_screen;
3044 height_scaled = video.screen_height;
3045 xpos = (video.screen_width - width_scaled) / 2;
3049 SDL_Rect src_rect = { 0, 0, width, height };
3050 SDL_Rect dst_rect = { xpos, ypos, width_scaled, height_scaled };
3052 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);