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 */
21 /* ========================================================================= */
23 /* ========================================================================= */
25 /* SDL internal variables */
26 #if defined(TARGET_SDL2)
27 static SDL_Window *sdl_window = NULL;
28 static SDL_Renderer *sdl_renderer = NULL;
29 static SDL_Texture *sdl_texture_stream = NULL;
30 static SDL_Texture *sdl_texture_target = NULL;
31 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();
45 void SDLLimitScreenUpdates(boolean enable)
47 limit_screen_updates = enable;
50 static void FinalizeScreen(int draw_target)
52 // copy global animations to render target buffer, if defined (below border)
53 if (gfx.draw_global_anim_function != NULL)
54 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
56 // copy global masked border to render target buffer, if defined
57 if (gfx.draw_global_border_function != NULL)
58 gfx.draw_global_border_function(draw_target);
60 // copy global animations to render target buffer, if defined (above border)
61 if (gfx.draw_global_anim_function != NULL)
62 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
65 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
67 static unsigned int update_screen_delay = 0;
68 unsigned int update_screen_delay_value = 50; /* (milliseconds) */
69 SDL_Surface *screen = backbuffer->surface;
71 if (limit_screen_updates &&
72 !DelayReached(&update_screen_delay, update_screen_delay_value))
75 LimitScreenUpdates(FALSE);
79 static int LastFrameCounter = 0;
80 boolean changed = (FrameCounter != LastFrameCounter);
82 printf("::: FrameCounter == %d [%s]\n", FrameCounter,
83 (changed ? "-" : "SAME FRAME UPDATED"));
85 LastFrameCounter = FrameCounter;
94 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
95 gfx.final_screen_bitmap != NULL) // may not be initialized yet
97 // draw global animations using bitmaps instead of using textures
98 // to prevent texture scaling artefacts (this is potentially slower)
100 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
101 gfx.win_xsize, gfx.win_ysize, 0, 0);
103 FinalizeScreen(DRAW_TO_SCREEN);
105 screen = gfx.final_screen_bitmap->surface;
107 // force full window redraw
111 #if defined(TARGET_SDL2)
112 SDL_Texture *sdl_texture = sdl_texture_stream;
114 // deactivate use of target texture if render targets are not supported
115 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
116 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
117 sdl_texture_target == NULL)
118 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
120 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
121 sdl_texture = sdl_texture_target;
125 int bytes_x = screen->pitch / video.width;
126 int bytes_y = screen->pitch;
128 SDL_UpdateTexture(sdl_texture, rect,
129 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
134 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
137 int xoff = video.screen_xoffset;
138 int yoff = video.screen_yoffset;
139 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
140 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
141 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
143 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
144 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
145 dst_rect2 = &dst_rect_screen;
147 dst_rect1 = &dst_rect_screen;
149 #if defined(HAS_SCREEN_KEYBOARD)
150 if (video.shifted_up || video.shifted_up_delay)
152 int time_current = SDL_GetTicks();
153 int pos = video.shifted_up_pos;
154 int pos_last = video.shifted_up_pos_last;
156 if (!DelayReachedExt(&video.shifted_up_delay, video.shifted_up_delay_value,
159 int delay = time_current - video.shifted_up_delay;
160 int delay_value = video.shifted_up_delay_value;
162 pos = pos_last + (pos - pos_last) * delay / delay_value;
166 video.shifted_up_pos_last = pos;
167 video.shifted_up_delay = 0;
170 SDL_Rect src_rect_up = { 0, pos, video.width, video.height - pos };
171 SDL_Rect dst_rect_up = { xoff, yoff, video.width, video.height - pos };
173 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
174 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
176 src_rect2 = &src_rect_up;
177 dst_rect2 = &dst_rect_up;
181 src_rect1 = &src_rect_up;
182 dst_rect1 = &dst_rect_up;
187 // clear render target buffer
188 SDL_RenderClear(sdl_renderer);
190 // set renderer to use target texture for rendering
191 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
192 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
193 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
195 // copy backbuffer texture to render target buffer
196 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
197 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
199 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
200 FinalizeScreen(DRAW_TO_SCREEN);
202 // when using target texture, copy it to screen buffer
203 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
204 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
206 SDL_SetRenderTarget(sdl_renderer, NULL);
207 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
210 #if defined(USE_TOUCH_INPUT_OVERLAY)
211 // draw overlay graphics for touch device input, if needed
212 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 #if defined(TARGET_SDL2)
222 // show render target buffer on screen
223 SDL_RenderPresent(sdl_renderer);
226 SDL_UpdateRects(screen, 1, rect);
228 SDL_UpdateRect(screen, 0, 0, 0, 0);
232 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
234 UpdateScreenExt(rect, TRUE);
237 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
239 UpdateScreenExt(rect, FALSE);
242 static void SDLSetWindowIcon(char *basename)
244 /* (setting the window icon on Mac OS X would replace the high-quality
245 dock icon with the currently smaller (and uglier) icon from file) */
247 #if !defined(PLATFORM_MACOSX)
248 char *filename = getCustomImageFilename(basename);
249 SDL_Surface *surface;
251 if (filename == NULL)
253 Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
258 if ((surface = IMG_Load(filename)) == NULL)
260 Error(ERR_WARN, "IMG_Load() failed: %s", SDL_GetError());
265 /* set transparent color */
266 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
267 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
269 #if defined(TARGET_SDL2)
270 SDL_SetWindowIcon(sdl_window, surface);
272 SDL_WM_SetIcon(surface, NULL);
277 #if defined(TARGET_SDL2)
279 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
280 SDL_PixelFormat *format2)
282 return (format1->format == format2->format &&
283 format1->BitsPerPixel == format2->BitsPerPixel &&
284 format1->BytesPerPixel == format2->BytesPerPixel &&
285 format1->Rmask == format2->Rmask &&
286 format1->Gmask == format2->Gmask &&
287 format1->Bmask == format2->Bmask);
290 static Pixel SDLGetColorKey(SDL_Surface *surface)
294 if (SDL_GetColorKey(surface, &color_key) != 0)
300 static boolean SDLHasColorKey(SDL_Surface *surface)
302 return (SDLGetColorKey(surface) != -1);
305 static boolean SDLHasAlpha(SDL_Surface *surface)
307 SDL_BlendMode blend_mode;
309 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
312 return (blend_mode == SDL_BLENDMODE_BLEND);
315 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
317 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
319 SDL_SetSurfaceBlendMode(surface, blend_mode);
320 SDL_SetSurfaceAlphaMod(surface, alpha);
323 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
325 SDL_PixelFormat format;
326 SDL_Surface *new_surface;
331 if (backbuffer && backbuffer->surface)
333 format = *backbuffer->surface->format;
334 format.Amask = surface->format->Amask; // keep alpha channel
338 format = *surface->format;
341 new_surface = SDL_ConvertSurface(surface, &format, 0);
343 if (new_surface == NULL)
344 Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
349 boolean SDLSetNativeSurface(SDL_Surface **surface)
351 SDL_Surface *new_surface;
353 if (surface == NULL ||
355 backbuffer == NULL ||
356 backbuffer->surface == NULL)
359 // if pixel format already optimized for destination surface, do nothing
360 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
363 new_surface = SDLGetNativeSurface(*surface);
365 SDL_FreeSurface(*surface);
367 *surface = new_surface;
374 static Pixel SDLGetColorKey(SDL_Surface *surface)
376 if ((surface->flags & SDL_SRCCOLORKEY) == 0)
379 return surface->format->colorkey;
382 static boolean SDLHasColorKey(SDL_Surface *surface)
384 return (SDLGetColorKey(surface) != -1);
387 static boolean SDLHasAlpha(SDL_Surface *surface)
389 return ((surface->flags & SDL_SRCALPHA) != 0);
392 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
394 SDL_SetAlpha(surface, (set ? SDL_SRCALPHA : 0), alpha);
397 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
399 SDL_Surface *new_surface;
404 if (!video.initialized)
405 new_surface = SDL_ConvertSurface(surface, surface->format, SURFACE_FLAGS);
406 else if (SDLHasAlpha(surface))
407 new_surface = SDL_DisplayFormatAlpha(surface);
409 new_surface = SDL_DisplayFormat(surface);
411 if (new_surface == NULL)
412 Error(ERR_EXIT, "%s() failed: %s",
413 (video.initialized ? "SDL_DisplayFormat" : "SDL_ConvertSurface"),
419 boolean SDLSetNativeSurface(SDL_Surface **surface)
421 SDL_Surface *new_surface;
423 if (surface == NULL ||
428 new_surface = SDLGetNativeSurface(*surface);
430 SDL_FreeSurface(*surface);
432 *surface = new_surface;
439 #if defined(TARGET_SDL2)
440 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
442 if (program.headless)
445 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
448 Error(ERR_EXIT, "SDL_CreateTextureFromSurface() failed: %s",
455 void SDLCreateBitmapTextures(Bitmap *bitmap)
457 #if defined(TARGET_SDL2)
462 SDL_DestroyTexture(bitmap->texture);
463 if (bitmap->texture_masked)
464 SDL_DestroyTexture(bitmap->texture_masked);
466 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
467 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
471 void SDLFreeBitmapTextures(Bitmap *bitmap)
473 #if defined(TARGET_SDL2)
478 SDL_DestroyTexture(bitmap->texture);
479 if (bitmap->texture_masked)
480 SDL_DestroyTexture(bitmap->texture_masked);
482 bitmap->texture = NULL;
483 bitmap->texture_masked = NULL;
487 void SDLInitVideoDisplay(void)
489 #if !defined(TARGET_SDL2)
490 if (!strEqual(setup.system.sdl_videodriver, ARG_DEFAULT))
491 SDL_putenv(getStringCat2("SDL_VIDEODRIVER=", setup.system.sdl_videodriver));
493 SDL_putenv("SDL_VIDEO_CENTERED=1");
496 /* initialize SDL video */
497 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
498 Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
500 /* set default SDL depth */
501 #if !defined(TARGET_SDL2)
502 video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
504 video.default_depth = 32; // (how to determine video depth in SDL2?)
508 inline static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
510 if (program.headless)
513 video.window_scaling_percent = setup.window_scaling_percent;
514 video.window_scaling_quality = setup.window_scaling_quality;
516 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
518 #if defined(TARGET_SDL2)
519 // SDL 2.0: support for (desktop) fullscreen mode available
520 video.fullscreen_available = TRUE;
522 // SDL 1.2: no support for fullscreen mode in R'n'D anymore
523 video.fullscreen_available = FALSE;
526 /* open SDL video output device (window or fullscreen mode) */
527 if (!SDLSetVideoMode(fullscreen))
528 Error(ERR_EXIT, "setting video mode failed");
530 /* !!! SDL2 can only set the window icon if the window already exists !!! */
531 /* set window icon */
532 SDLSetWindowIcon(program.icon_filename);
534 /* set window and icon title */
535 #if defined(TARGET_SDL2)
536 SDL_SetWindowTitle(sdl_window, program.window_title);
538 SDL_WM_SetCaption(program.window_title, program.window_title);
542 inline static void SDLInitVideoBuffer_DrawBuffer()
544 /* SDL cannot directly draw to the visible video framebuffer like X11,
545 but always uses a backbuffer, which is then blitted to the visible
546 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
547 visible video framebuffer with 'SDL_Flip', if the hardware supports
548 this). Therefore do not use an additional backbuffer for drawing, but
549 use a symbolic buffer (distinguishable from the SDL backbuffer) called
550 'window', which indicates that the SDL backbuffer should be updated to
551 the visible video framebuffer when attempting to blit to it.
553 For convenience, it seems to be a good idea to create this symbolic
554 buffer 'window' at the same size as the SDL backbuffer. Although it
555 should never be drawn to directly, it would do no harm nevertheless. */
557 /* create additional (symbolic) buffer for double-buffering */
558 ReCreateBitmap(&window, video.width, video.height);
560 /* create dummy drawing buffer for headless mode, if needed */
561 if (program.headless)
562 ReCreateBitmap(&backbuffer, video.width, video.height);
565 void SDLInitVideoBuffer(boolean fullscreen)
567 SDLInitVideoBuffer_VideoBuffer(fullscreen);
568 SDLInitVideoBuffer_DrawBuffer();
571 static boolean SDLCreateScreen(boolean fullscreen)
573 SDL_Surface *new_surface = NULL;
575 #if defined(TARGET_SDL2)
576 int surface_flags_window = SURFACE_FLAGS | SDL_WINDOW_RESIZABLE;
577 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
579 int surface_flags_window = SURFACE_FLAGS;
580 int surface_flags_fullscreen = SURFACE_FLAGS; // (no fullscreen in SDL 1.2)
583 #if defined(TARGET_SDL2)
585 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
587 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
588 _without_ enabling 2D/3D acceleration and/or guest additions installed,
589 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
590 it will try to use accelerated graphics and apparently fails miserably) */
591 int renderer_flags = SDL_RENDERER_SOFTWARE;
594 SDLSetScreenSizeAndOffsets(video.width, video.height);
597 int width = video.width;
598 int height = video.height;
599 int screen_width = video.screen_width;
600 int screen_height = video.screen_height;
601 int surface_flags = (fullscreen ? surface_flags_fullscreen :
602 surface_flags_window);
604 // default window size is unscaled
605 video.window_width = screen_width;
606 video.window_height = screen_height;
608 #if defined(TARGET_SDL2)
610 // store if initial screen mode is fullscreen mode when changing screen size
611 video.fullscreen_initial = fullscreen;
613 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
615 video.window_width = window_scaling_factor * screen_width;
616 video.window_height = window_scaling_factor * screen_height;
618 if (sdl_texture_stream)
620 SDL_DestroyTexture(sdl_texture_stream);
621 sdl_texture_stream = NULL;
624 if (sdl_texture_target)
626 SDL_DestroyTexture(sdl_texture_target);
627 sdl_texture_target = NULL;
630 if (!(fullscreen && fullscreen_enabled))
634 SDL_DestroyRenderer(sdl_renderer);
640 SDL_DestroyWindow(sdl_window);
645 if (sdl_window == NULL)
646 sdl_window = SDL_CreateWindow(program.window_title,
647 SDL_WINDOWPOS_CENTERED,
648 SDL_WINDOWPOS_CENTERED,
653 if (sdl_window != NULL)
655 if (sdl_renderer == NULL)
656 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
658 if (sdl_renderer != NULL)
660 SDL_RenderSetLogicalSize(sdl_renderer, screen_width, screen_height);
661 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
662 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
664 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
665 SDL_PIXELFORMAT_ARGB8888,
666 SDL_TEXTUREACCESS_STREAMING,
669 if (SDL_RenderTargetSupported(sdl_renderer))
670 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
671 SDL_PIXELFORMAT_ARGB8888,
672 SDL_TEXTUREACCESS_TARGET,
675 if (sdl_texture_stream != NULL)
677 // use SDL default values for RGB masks and no alpha channel
678 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
680 if (new_surface == NULL)
681 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
685 Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
690 Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
695 Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
700 if (gfx.final_screen_bitmap == NULL)
701 gfx.final_screen_bitmap = CreateBitmapStruct();
703 gfx.final_screen_bitmap->width = width;
704 gfx.final_screen_bitmap->height = height;
706 gfx.final_screen_bitmap->surface =
707 SDL_SetVideoMode(width, height, video.depth, surface_flags);
709 if (gfx.final_screen_bitmap->surface != NULL)
712 SDL_CreateRGBSurface(surface_flags, width, height, video.depth, 0,0,0, 0);
714 if (new_surface == NULL)
715 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
718 new_surface = gfx.final_screen_bitmap->surface;
719 gfx.final_screen_bitmap = NULL;
725 Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
729 #if defined(TARGET_SDL2)
730 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
731 if (new_surface != NULL)
732 fullscreen_enabled = fullscreen;
735 if (backbuffer == NULL)
736 backbuffer = CreateBitmapStruct();
738 backbuffer->width = video.width;
739 backbuffer->height = video.height;
741 if (backbuffer->surface)
742 SDL_FreeSurface(backbuffer->surface);
744 backbuffer->surface = new_surface;
746 return (new_surface != NULL);
749 boolean SDLSetVideoMode(boolean fullscreen)
751 boolean success = FALSE;
755 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
757 /* switch display to fullscreen mode, if available */
758 success = SDLCreateScreen(TRUE);
762 /* switching display to fullscreen mode failed -- do not try it again */
763 video.fullscreen_available = FALSE;
767 video.fullscreen_enabled = TRUE;
771 if ((!fullscreen && video.fullscreen_enabled) || !success)
773 /* switch display to window mode */
774 success = SDLCreateScreen(FALSE);
778 /* switching display to window mode failed -- should not happen */
782 video.fullscreen_enabled = FALSE;
783 video.window_scaling_percent = setup.window_scaling_percent;
784 video.window_scaling_quality = setup.window_scaling_quality;
786 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
790 #if defined(TARGET_SDL2)
791 SDLRedrawWindow(); // map window
795 #if defined(PLATFORM_WIN32)
796 // experimental drag and drop code
798 SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
801 SDL_SysWMinfo wminfo;
803 boolean wminfo_success = FALSE;
805 SDL_VERSION(&wminfo.version);
806 #if defined(TARGET_SDL2)
808 wminfo_success = SDL_GetWindowWMInfo(sdl_window, &wminfo);
810 wminfo_success = (SDL_GetWMInfo(&wminfo) == 1);
815 #if defined(TARGET_SDL2)
816 hwnd = wminfo.info.win.window;
818 hwnd = wminfo.window;
821 DragAcceptFiles(hwnd, TRUE);
830 void SDLSetWindowTitle()
832 #if defined(TARGET_SDL2)
833 SDL_SetWindowTitle(sdl_window, program.window_title);
835 SDL_WM_SetCaption(program.window_title, program.window_title);
839 #if defined(TARGET_SDL2)
840 void SDLSetWindowScaling(int window_scaling_percent)
842 if (sdl_window == NULL)
845 float window_scaling_factor = (float)window_scaling_percent / 100;
846 int new_window_width = (int)(window_scaling_factor * video.screen_width);
847 int new_window_height = (int)(window_scaling_factor * video.screen_height);
849 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
851 video.window_scaling_percent = window_scaling_percent;
852 video.window_width = new_window_width;
853 video.window_height = new_window_height;
858 void SDLSetWindowScalingQuality(char *window_scaling_quality)
860 SDL_Texture *new_texture;
862 if (sdl_texture_stream == NULL)
865 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
867 new_texture = SDL_CreateTexture(sdl_renderer,
868 SDL_PIXELFORMAT_ARGB8888,
869 SDL_TEXTUREACCESS_STREAMING,
870 video.width, video.height);
872 if (new_texture != NULL)
874 SDL_DestroyTexture(sdl_texture_stream);
876 sdl_texture_stream = new_texture;
879 if (SDL_RenderTargetSupported(sdl_renderer))
880 new_texture = SDL_CreateTexture(sdl_renderer,
881 SDL_PIXELFORMAT_ARGB8888,
882 SDL_TEXTUREACCESS_TARGET,
883 video.width, video.height);
887 if (new_texture != NULL)
889 SDL_DestroyTexture(sdl_texture_target);
891 sdl_texture_target = new_texture;
896 video.window_scaling_quality = window_scaling_quality;
899 void SDLSetWindowFullscreen(boolean fullscreen)
901 if (sdl_window == NULL)
904 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
906 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
907 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
909 // if screen size was changed in fullscreen mode, correct desktop window size
910 if (!fullscreen && video.fullscreen_initial)
912 SDLSetWindowScaling(setup.window_scaling_percent);
913 SDL_SetWindowPosition(sdl_window,
914 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
916 video.fullscreen_initial = FALSE;
920 void SDLSetDisplaySize()
922 SDL_Rect display_bounds;
924 SDL_GetDisplayBounds(0, &display_bounds);
926 video.display_width = display_bounds.w;
927 video.display_height = display_bounds.h;
930 Error(ERR_DEBUG, "SDL real screen size: %d x %d",
931 video.display_width, video.display_height);
935 void SDLSetScreenSizeAndOffsets(int width, int height)
937 // set default video screen size and offsets
938 video.screen_width = width;
939 video.screen_height = height;
940 video.screen_xoffset = 0;
941 video.screen_yoffset = 0;
943 #if defined(USE_COMPLETE_DISPLAY)
944 float ratio_video = (float) width / height;
945 float ratio_display = (float) video.display_width / video.display_height;
947 if (ratio_video != ratio_display)
949 // adjust drawable screen size to cover the whole device display
951 if (ratio_video < ratio_display)
952 video.screen_width *= ratio_display / ratio_video;
954 video.screen_height *= ratio_video / ratio_display;
956 video.screen_xoffset = (video.screen_width - width) / 2;
957 video.screen_yoffset = (video.screen_height - height) / 2;
960 Error(ERR_DEBUG, "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
962 video.screen_width, video.screen_height,
963 ratio_video, ratio_display);
969 void SDLSetScreenSizeForRenderer(int width, int height)
971 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
974 void SDLSetScreenProperties()
976 SDLSetScreenSizeAndOffsets(video.width, video.height);
977 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
982 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
984 #if defined(TARGET_SDL2)
985 video.screen_rendering_mode =
986 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
987 SPECIAL_RENDERING_BITMAP :
988 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
989 SPECIAL_RENDERING_TARGET:
990 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
991 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
993 video.screen_rendering_mode = SPECIAL_RENDERING_BITMAP;
997 void SDLRedrawWindow()
999 UpdateScreen_WithoutFrameDelay(NULL);
1002 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
1005 if (program.headless)
1008 SDL_Surface *surface =
1009 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
1011 if (surface == NULL)
1012 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
1014 SDLSetNativeSurface(&surface);
1016 bitmap->surface = surface;
1019 void SDLFreeBitmapPointers(Bitmap *bitmap)
1021 if (bitmap->surface)
1022 SDL_FreeSurface(bitmap->surface);
1023 if (bitmap->surface_masked)
1024 SDL_FreeSurface(bitmap->surface_masked);
1026 bitmap->surface = NULL;
1027 bitmap->surface_masked = NULL;
1029 #if defined(TARGET_SDL2)
1030 if (bitmap->texture)
1031 SDL_DestroyTexture(bitmap->texture);
1032 if (bitmap->texture_masked)
1033 SDL_DestroyTexture(bitmap->texture_masked);
1035 bitmap->texture = NULL;
1036 bitmap->texture_masked = NULL;
1040 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1041 int src_x, int src_y, int width, int height,
1042 int dst_x, int dst_y, int mask_mode)
1044 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1045 SDL_Rect src_rect, dst_rect;
1050 src_rect.h = height;
1055 dst_rect.h = height;
1057 // if (src_bitmap != backbuffer || dst_bitmap != window)
1058 if (!(src_bitmap == backbuffer && dst_bitmap == window))
1059 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
1060 src_bitmap->surface_masked : src_bitmap->surface),
1061 &src_rect, real_dst_bitmap->surface, &dst_rect);
1063 if (dst_bitmap == window)
1064 UpdateScreen_WithFrameDelay(&dst_rect);
1067 void SDLBlitTexture(Bitmap *bitmap,
1068 int src_x, int src_y, int width, int height,
1069 int dst_x, int dst_y, int mask_mode)
1071 #if defined(TARGET_SDL2)
1072 SDL_Texture *texture;
1077 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1079 if (texture == NULL)
1085 src_rect.h = height;
1090 dst_rect.h = height;
1092 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1096 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1099 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1107 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1109 if (dst_bitmap == window)
1110 UpdateScreen_WithFrameDelay(&rect);
1113 void PrepareFadeBitmap(int draw_target)
1115 Bitmap *fade_bitmap =
1116 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1117 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1119 if (fade_bitmap == NULL)
1122 // copy backbuffer to fading buffer
1123 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1125 // add border and animations to fading buffer
1126 FinalizeScreen(draw_target);
1129 void SDLFadeRectangle(int x, int y, int width, int height,
1130 int fade_mode, int fade_delay, int post_delay,
1131 void (*draw_border_function)(void))
1133 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1134 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1135 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1136 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1137 SDL_Surface *surface_screen = backbuffer->surface;
1138 SDL_Rect src_rect, dst_rect;
1140 int src_x = x, src_y = y;
1141 int dst_x = x, dst_y = y;
1142 unsigned int time_last, time_current;
1144 // store function for drawing global masked border
1145 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1147 // deactivate drawing of global border while fading, if needed
1148 if (draw_border_function == NULL)
1149 gfx.draw_global_border_function = NULL;
1154 src_rect.h = height;
1158 dst_rect.w = width; /* (ignored) */
1159 dst_rect.h = height; /* (ignored) */
1161 dst_rect2 = dst_rect;
1163 // before fading in, store backbuffer (without animation graphics)
1164 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1165 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1167 /* copy source and target surfaces to temporary surfaces for fading */
1168 if (fade_mode & FADE_TYPE_TRANSFORM)
1170 // (source and target fading buffer already prepared)
1172 else if (fade_mode & FADE_TYPE_FADE_IN)
1174 // (target fading buffer already prepared)
1175 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1177 else /* FADE_TYPE_FADE_OUT */
1179 // (source fading buffer already prepared)
1180 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1183 time_current = SDL_GetTicks();
1185 if (fade_mode == FADE_MODE_MELT)
1187 boolean done = FALSE;
1188 int melt_pixels = 2;
1189 int melt_columns = width / melt_pixels;
1190 int ypos[melt_columns];
1191 int max_steps = height / 8 + 32;
1196 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1198 SDLSetAlpha(surface_target, FALSE, 0); /* disable alpha blending */
1200 ypos[0] = -GetSimpleRandom(16);
1202 for (i = 1 ; i < melt_columns; i++)
1204 int r = GetSimpleRandom(3) - 1; /* randomly choose from { -1, 0, -1 } */
1206 ypos[i] = ypos[i - 1] + r;
1219 time_last = time_current;
1220 time_current = SDL_GetTicks();
1221 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1222 steps_final = MIN(MAX(0, steps), max_steps);
1226 done = (steps_done >= steps_final);
1228 for (i = 0 ; i < melt_columns; i++)
1236 else if (ypos[i] < height)
1241 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1243 if (ypos[i] + dy >= height)
1244 dy = height - ypos[i];
1246 /* copy part of (appearing) target surface to upper area */
1247 src_rect.x = src_x + i * melt_pixels;
1248 // src_rect.y = src_y + ypos[i];
1250 src_rect.w = melt_pixels;
1252 src_rect.h = ypos[i] + dy;
1254 dst_rect.x = dst_x + i * melt_pixels;
1255 // dst_rect.y = dst_y + ypos[i];
1258 if (steps_done >= steps_final)
1259 SDL_BlitSurface(surface_target, &src_rect,
1260 surface_screen, &dst_rect);
1264 /* copy part of (disappearing) source surface to lower area */
1265 src_rect.x = src_x + i * melt_pixels;
1267 src_rect.w = melt_pixels;
1268 src_rect.h = height - ypos[i];
1270 dst_rect.x = dst_x + i * melt_pixels;
1271 dst_rect.y = dst_y + ypos[i];
1273 if (steps_done >= steps_final)
1274 SDL_BlitSurface(surface_source, &src_rect,
1275 surface_screen, &dst_rect);
1281 src_rect.x = src_x + i * melt_pixels;
1283 src_rect.w = melt_pixels;
1284 src_rect.h = height;
1286 dst_rect.x = dst_x + i * melt_pixels;
1289 if (steps_done >= steps_final)
1290 SDL_BlitSurface(surface_target, &src_rect,
1291 surface_screen, &dst_rect);
1295 if (steps_done >= steps_final)
1297 if (draw_border_function != NULL)
1298 draw_border_function();
1300 UpdateScreen_WithFrameDelay(&dst_rect2);
1304 else if (fade_mode == FADE_MODE_CURTAIN)
1308 int xx_size = width / 2;
1310 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1312 SDLSetAlpha(surface_source, FALSE, 0); /* disable alpha blending */
1314 for (xx = 0; xx < xx_size;)
1316 time_last = time_current;
1317 time_current = SDL_GetTicks();
1318 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1319 xx_final = MIN(MAX(0, xx), xx_size);
1324 src_rect.h = height;
1329 /* draw new (target) image to screen buffer */
1330 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1332 if (xx_final < xx_size)
1334 src_rect.w = xx_size - xx_final;
1335 src_rect.h = height;
1337 /* draw old (source) image to screen buffer (left side) */
1339 src_rect.x = src_x + xx_final;
1342 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1344 /* draw old (source) image to screen buffer (right side) */
1346 src_rect.x = src_x + xx_size;
1347 dst_rect.x = dst_x + xx_size + xx_final;
1349 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1352 if (draw_border_function != NULL)
1353 draw_border_function();
1355 /* only update the region of the screen that is affected from fading */
1356 UpdateScreen_WithFrameDelay(&dst_rect2);
1359 else /* fading in, fading out or cross-fading */
1364 for (alpha = 0.0; alpha < 255.0;)
1366 time_last = time_current;
1367 time_current = SDL_GetTicks();
1368 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1369 alpha_final = MIN(MAX(0, alpha), 255);
1371 /* draw existing (source) image to screen buffer */
1372 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1374 /* draw new (target) image to screen buffer using alpha blending */
1375 SDLSetAlpha(surface_target, TRUE, alpha_final);
1376 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1378 if (draw_border_function != NULL)
1379 draw_border_function();
1381 /* only update the region of the screen that is affected from fading */
1382 UpdateScreen_WithFrameDelay(&dst_rect);
1388 unsigned int time_post_delay;
1390 time_current = SDL_GetTicks();
1391 time_post_delay = time_current + post_delay;
1393 while (time_current < time_post_delay)
1395 // updating the screen contains waiting for frame delay (non-busy)
1396 UpdateScreen_WithFrameDelay(NULL);
1398 time_current = SDL_GetTicks();
1402 // restore function for drawing global masked border
1403 gfx.draw_global_border_function = draw_global_border_function;
1405 // after fading in, restore backbuffer (without animation graphics)
1406 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1407 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1410 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1411 int to_x, int to_y, Uint32 color)
1413 SDL_Surface *surface = dst_bitmap->surface;
1417 swap_numbers(&from_x, &to_x);
1420 swap_numbers(&from_y, &to_y);
1424 rect.w = (to_x - from_x + 1);
1425 rect.h = (to_y - from_y + 1);
1427 SDL_FillRect(surface, &rect, color);
1430 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1431 int to_x, int to_y, Uint32 color)
1433 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1436 #if ENABLE_UNUSED_CODE
1437 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1438 int num_points, Uint32 color)
1443 for (i = 0; i < num_points - 1; i++)
1445 for (x = 0; x < line_width; x++)
1447 for (y = 0; y < line_width; y++)
1449 int dx = x - line_width / 2;
1450 int dy = y - line_width / 2;
1452 if ((x == 0 && y == 0) ||
1453 (x == 0 && y == line_width - 1) ||
1454 (x == line_width - 1 && y == 0) ||
1455 (x == line_width - 1 && y == line_width - 1))
1458 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1459 points[i+1].x + dx, points[i+1].y + dy, color);
1466 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1468 SDL_Surface *surface = src_bitmap->surface;
1470 switch (surface->format->BytesPerPixel)
1472 case 1: /* assuming 8-bpp */
1474 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1478 case 2: /* probably 15-bpp or 16-bpp */
1480 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1484 case 3: /* slow 24-bpp mode; usually not used */
1486 /* does this work? */
1487 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1491 shift = surface->format->Rshift;
1492 color |= *(pix + shift / 8) >> shift;
1493 shift = surface->format->Gshift;
1494 color |= *(pix + shift / 8) >> shift;
1495 shift = surface->format->Bshift;
1496 color |= *(pix + shift / 8) >> shift;
1502 case 4: /* probably 32-bpp */
1504 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1513 /* ========================================================================= */
1514 /* The following functions were taken from the SGE library */
1515 /* (SDL Graphics Extension Library) by Anders Lindström */
1516 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html */
1517 /* ========================================================================= */
1519 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1521 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1523 switch (surface->format->BytesPerPixel)
1527 /* Assuming 8-bpp */
1528 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1534 /* Probably 15-bpp or 16-bpp */
1535 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1541 /* Slow 24-bpp mode, usually not used */
1545 /* Gack - slow, but endian correct */
1546 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1547 shift = surface->format->Rshift;
1548 *(pix+shift/8) = color>>shift;
1549 shift = surface->format->Gshift;
1550 *(pix+shift/8) = color>>shift;
1551 shift = surface->format->Bshift;
1552 *(pix+shift/8) = color>>shift;
1558 /* Probably 32-bpp */
1559 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1566 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1567 Uint8 R, Uint8 G, Uint8 B)
1569 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1572 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1574 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1577 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1579 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1582 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1587 /* Gack - slow, but endian correct */
1588 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1589 shift = surface->format->Rshift;
1590 *(pix+shift/8) = color>>shift;
1591 shift = surface->format->Gshift;
1592 *(pix+shift/8) = color>>shift;
1593 shift = surface->format->Bshift;
1594 *(pix+shift/8) = color>>shift;
1597 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1599 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1602 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1604 switch (dest->format->BytesPerPixel)
1607 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1611 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1615 _PutPixel24(dest,x,y,color);
1619 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1624 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1626 if (SDL_MUSTLOCK(surface))
1628 if (SDL_LockSurface(surface) < 0)
1634 _PutPixel(surface, x, y, color);
1636 if (SDL_MUSTLOCK(surface))
1638 SDL_UnlockSurface(surface);
1642 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1643 Uint8 r, Uint8 g, Uint8 b)
1645 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1648 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1650 if (y >= 0 && y <= dest->h - 1)
1652 switch (dest->format->BytesPerPixel)
1655 return y*dest->pitch;
1659 return y*dest->pitch/2;
1663 return y*dest->pitch;
1667 return y*dest->pitch/4;
1675 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
1677 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1679 switch (surface->format->BytesPerPixel)
1683 /* Assuming 8-bpp */
1684 *((Uint8 *)surface->pixels + ypitch + x) = color;
1690 /* Probably 15-bpp or 16-bpp */
1691 *((Uint16 *)surface->pixels + ypitch + x) = color;
1697 /* Slow 24-bpp mode, usually not used */
1701 /* Gack - slow, but endian correct */
1702 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1703 shift = surface->format->Rshift;
1704 *(pix+shift/8) = color>>shift;
1705 shift = surface->format->Gshift;
1706 *(pix+shift/8) = color>>shift;
1707 shift = surface->format->Bshift;
1708 *(pix+shift/8) = color>>shift;
1714 /* Probably 32-bpp */
1715 *((Uint32 *)surface->pixels + ypitch + x) = color;
1722 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1727 if (SDL_MUSTLOCK(Surface))
1729 if (SDL_LockSurface(Surface) < 0)
1742 /* Do the clipping */
1743 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1747 if (x2 > Surface->w - 1)
1748 x2 = Surface->w - 1;
1755 SDL_FillRect(Surface, &l, Color);
1757 if (SDL_MUSTLOCK(Surface))
1759 SDL_UnlockSurface(Surface);
1763 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1764 Uint8 R, Uint8 G, Uint8 B)
1766 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1769 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
1780 /* Do the clipping */
1781 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1785 if (x2 > Surface->w - 1)
1786 x2 = Surface->w - 1;
1793 SDL_FillRect(Surface, &l, Color);
1796 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1801 if (SDL_MUSTLOCK(Surface))
1803 if (SDL_LockSurface(Surface) < 0)
1816 /* Do the clipping */
1817 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1821 if (y2 > Surface->h - 1)
1822 y2 = Surface->h - 1;
1829 SDL_FillRect(Surface, &l, Color);
1831 if (SDL_MUSTLOCK(Surface))
1833 SDL_UnlockSurface(Surface);
1837 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1838 Uint8 R, Uint8 G, Uint8 B)
1840 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1843 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
1854 /* Do the clipping */
1855 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1859 if (y2 > Surface->h - 1)
1860 y2 = Surface->h - 1;
1867 SDL_FillRect(Surface, &l, Color);
1870 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1871 Sint16 x2, Sint16 y2, Uint32 Color,
1872 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1875 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1880 sdx = (dx < 0) ? -1 : 1;
1881 sdy = (dy < 0) ? -1 : 1;
1893 for (x = 0; x < dx; x++)
1895 Callback(Surface, px, py, Color);
1909 for (y = 0; y < dy; y++)
1911 Callback(Surface, px, py, Color);
1925 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1926 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1927 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1930 sge_DoLine(Surface, X1, Y1, X2, Y2,
1931 SDL_MapRGB(Surface->format, R, G, B), Callback);
1934 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1937 if (SDL_MUSTLOCK(Surface))
1939 if (SDL_LockSurface(Surface) < 0)
1944 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1946 /* unlock the display */
1947 if (SDL_MUSTLOCK(Surface))
1949 SDL_UnlockSurface(Surface);
1953 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1954 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1956 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1959 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1961 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1966 -----------------------------------------------------------------------------
1967 quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1968 -----------------------------------------------------------------------------
1971 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1972 int width, int height, Uint32 color)
1976 for (y = src_y; y < src_y + height; y++)
1978 for (x = src_x; x < src_x + width; x++)
1980 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1982 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1987 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1988 int src_x, int src_y, int width, int height,
1989 int dst_x, int dst_y)
1993 for (y = 0; y < height; y++)
1995 for (x = 0; x < width; x++)
1997 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1999 if (pixel != BLACK_PIXEL)
2000 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
2006 /* ========================================================================= */
2007 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
2008 /* (Rotozoomer) by Andreas Schiffler */
2009 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html */
2010 /* ========================================================================= */
2013 -----------------------------------------------------------------------------
2016 zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
2017 -----------------------------------------------------------------------------
2028 int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
2031 tColorRGBA *sp, *csp, *dp;
2035 sp = csp = (tColorRGBA *) src->pixels;
2036 dp = (tColorRGBA *) dst->pixels;
2037 dgap = dst->pitch - dst->w * 4;
2039 for (y = 0; y < dst->h; y++)
2043 for (x = 0; x < dst->w; x++)
2045 tColorRGBA *sp0 = sp;
2046 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
2047 tColorRGBA *sp00 = &sp0[0];
2048 tColorRGBA *sp01 = &sp0[1];
2049 tColorRGBA *sp10 = &sp1[0];
2050 tColorRGBA *sp11 = &sp1[1];
2053 /* create new color pixel from all four source color pixels */
2054 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
2055 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
2056 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
2057 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
2062 /* advance source pointers */
2065 /* advance destination pointer */
2069 /* advance source pointer */
2070 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2072 /* advance destination pointers */
2073 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2079 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2081 int x, y, *sax, *say, *csax, *csay;
2083 tColorRGBA *sp, *csp, *csp0, *dp;
2086 /* use specialized zoom function when scaling down to exactly half size */
2087 if (src->w == 2 * dst->w &&
2088 src->h == 2 * dst->h)
2089 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2091 /* variable setup */
2092 sx = (float) src->w / (float) dst->w;
2093 sy = (float) src->h / (float) dst->h;
2095 /* allocate memory for row increments */
2096 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2097 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2099 /* precalculate row increments */
2100 for (x = 0; x <= dst->w; x++)
2101 *csax++ = (int)(sx * x);
2103 for (y = 0; y <= dst->h; y++)
2104 *csay++ = (int)(sy * y);
2107 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2108 dp = (tColorRGBA *) dst->pixels;
2109 dgap = dst->pitch - dst->w * 4;
2112 for (y = 0; y < dst->h; y++)
2117 for (x = 0; x < dst->w; x++)
2122 /* advance source pointers */
2126 /* advance destination pointer */
2130 /* advance source pointer */
2132 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2134 /* advance destination pointers */
2135 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2145 -----------------------------------------------------------------------------
2148 zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2149 -----------------------------------------------------------------------------
2152 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2154 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2155 Uint8 *sp, *dp, *csp;
2158 /* variable setup */
2159 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2160 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2162 /* allocate memory for row increments */
2163 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2164 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2166 /* precalculate row increments */
2169 for (x = 0; x < dst->w; x++)
2172 *csax = (csx >> 16);
2179 for (y = 0; y < dst->h; y++)
2182 *csay = (csy >> 16);
2189 for (x = 0; x < dst->w; x++)
2197 for (y = 0; y < dst->h; y++)
2204 sp = csp = (Uint8 *) src->pixels;
2205 dp = (Uint8 *) dst->pixels;
2206 dgap = dst->pitch - dst->w;
2210 for (y = 0; y < dst->h; y++)
2214 for (x = 0; x < dst->w; x++)
2219 /* advance source pointers */
2223 /* advance destination pointer */
2227 /* advance source pointer (for row) */
2228 csp += ((*csay) * src->pitch);
2231 /* advance destination pointers */
2242 -----------------------------------------------------------------------------
2245 Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2246 'zoomx' and 'zoomy' are scaling factors for width and height.
2247 If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2248 into a 32bit RGBA format on the fly.
2249 -----------------------------------------------------------------------------
2252 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2254 SDL_Surface *zoom_src = NULL;
2255 SDL_Surface *zoom_dst = NULL;
2256 boolean is_converted = FALSE;
2263 /* determine if source surface is 32 bit or 8 bit */
2264 is_32bit = (src->format->BitsPerPixel == 32);
2266 if (is_32bit || src->format->BitsPerPixel == 8)
2268 /* use source surface 'as is' */
2273 /* new source surface is 32 bit with a defined RGB ordering */
2274 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2275 0x000000ff, 0x0000ff00, 0x00ff0000,
2276 (src->format->Amask ? 0xff000000 : 0));
2277 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2279 is_converted = TRUE;
2282 /* allocate surface to completely contain the zoomed surface */
2285 /* target surface is 32 bit with source RGBA/ABGR ordering */
2286 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2287 zoom_src->format->Rmask,
2288 zoom_src->format->Gmask,
2289 zoom_src->format->Bmask,
2290 zoom_src->format->Amask);
2294 /* target surface is 8 bit */
2295 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2299 /* lock source surface */
2300 SDL_LockSurface(zoom_src);
2302 /* check which kind of surface we have */
2305 /* call the 32 bit transformation routine to do the zooming */
2306 zoomSurfaceRGBA(zoom_src, zoom_dst);
2311 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2312 zoom_dst->format->palette->colors[i] =
2313 zoom_src->format->palette->colors[i];
2314 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2316 /* call the 8 bit transformation routine to do the zooming */
2317 zoomSurfaceY(zoom_src, zoom_dst);
2320 /* unlock source surface */
2321 SDL_UnlockSurface(zoom_src);
2323 /* free temporary surface */
2325 SDL_FreeSurface(zoom_src);
2327 /* return destination surface */
2331 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2333 SDL_Surface *new_surface;
2335 if (surface == NULL)
2338 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2339 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2341 /* remove alpha channel from native non-transparent surface, if defined */
2342 SDLSetAlpha(new_surface, FALSE, 0);
2344 /* remove transparent color from native non-transparent surface, if defined */
2345 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2350 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2352 Bitmap *dst_bitmap = CreateBitmapStruct();
2353 SDL_Surface *src_surface = src_bitmap->surface_masked;
2354 SDL_Surface *dst_surface;
2356 dst_width = MAX(1, dst_width); /* prevent zero bitmap width */
2357 dst_height = MAX(1, dst_height); /* prevent zero bitmap height */
2359 dst_bitmap->width = dst_width;
2360 dst_bitmap->height = dst_height;
2362 /* create zoomed temporary surface from source surface */
2363 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2365 /* create native format destination surface from zoomed temporary surface */
2366 SDLSetNativeSurface(&dst_surface);
2368 /* set color key for zoomed surface from source surface, if defined */
2369 if (SDLHasColorKey(src_surface))
2370 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2371 SDLGetColorKey(src_surface));
2373 /* create native non-transparent surface for opaque blitting */
2374 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2376 /* set native transparent surface for masked blitting */
2377 dst_bitmap->surface_masked = dst_surface;
2383 /* ========================================================================= */
2384 /* load image to bitmap */
2385 /* ========================================================================= */
2387 Bitmap *SDLLoadImage(char *filename)
2389 Bitmap *new_bitmap = CreateBitmapStruct();
2390 SDL_Surface *sdl_image_tmp;
2392 if (program.headless)
2394 /* prevent sanity check warnings at later stage */
2395 new_bitmap->width = new_bitmap->height = 1;
2400 print_timestamp_init("SDLLoadImage");
2402 print_timestamp_time(getBaseNamePtr(filename));
2404 /* load image to temporary surface */
2405 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2406 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
2408 print_timestamp_time("IMG_Load");
2410 UPDATE_BUSY_STATE();
2412 /* create native non-transparent surface for current image */
2413 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2414 Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2416 print_timestamp_time("SDLGetNativeSurface (opaque)");
2418 UPDATE_BUSY_STATE();
2420 /* set black pixel to transparent if no alpha channel / transparent color */
2421 if (!SDLHasAlpha(sdl_image_tmp) &&
2422 !SDLHasColorKey(sdl_image_tmp))
2423 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2424 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2426 /* create native transparent surface for current image */
2427 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2428 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2430 print_timestamp_time("SDLGetNativeSurface (masked)");
2432 UPDATE_BUSY_STATE();
2434 /* free temporary surface */
2435 SDL_FreeSurface(sdl_image_tmp);
2437 new_bitmap->width = new_bitmap->surface->w;
2438 new_bitmap->height = new_bitmap->surface->h;
2440 print_timestamp_done("SDLLoadImage");
2446 /* ------------------------------------------------------------------------- */
2447 /* custom cursor fuctions */
2448 /* ------------------------------------------------------------------------- */
2450 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2452 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2453 cursor_info->width, cursor_info->height,
2454 cursor_info->hot_x, cursor_info->hot_y);
2457 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2459 static struct MouseCursorInfo *last_cursor_info = NULL;
2460 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2461 static SDL_Cursor *cursor_default = NULL;
2462 static SDL_Cursor *cursor_current = NULL;
2464 /* if invoked for the first time, store the SDL default cursor */
2465 if (cursor_default == NULL)
2466 cursor_default = SDL_GetCursor();
2468 /* only create new cursor if cursor info (custom only) has changed */
2469 if (cursor_info != NULL && cursor_info != last_cursor_info)
2471 cursor_current = create_cursor(cursor_info);
2472 last_cursor_info = cursor_info;
2475 /* only set new cursor if cursor info (custom or NULL) has changed */
2476 if (cursor_info != last_cursor_info2)
2477 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2479 last_cursor_info2 = cursor_info;
2483 /* ========================================================================= */
2484 /* audio functions */
2485 /* ========================================================================= */
2487 void SDLOpenAudio(void)
2489 if (program.headless)
2492 #if !defined(TARGET_SDL2)
2493 if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
2494 SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
2497 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2499 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2503 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2504 AUDIO_NUM_CHANNELS_STEREO,
2505 setup.system.audio_fragment_size) < 0)
2507 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2511 audio.sound_available = TRUE;
2512 audio.music_available = TRUE;
2513 audio.loops_available = TRUE;
2514 audio.sound_enabled = TRUE;
2516 /* set number of available mixer channels */
2517 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2518 audio.music_channel = MUSIC_CHANNEL;
2519 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2521 Mixer_InitChannels();
2524 void SDLCloseAudio(void)
2527 Mix_HaltChannel(-1);
2530 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2534 /* ========================================================================= */
2535 /* event functions */
2536 /* ========================================================================= */
2538 void SDLNextEvent(Event *event)
2540 SDL_WaitEvent(event);
2543 void SDLHandleWindowManagerEvent(Event *event)
2546 #if defined(PLATFORM_WIN32)
2547 // experimental drag and drop code
2549 SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
2550 SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
2552 #if defined(TARGET_SDL2)
2553 if (syswmmsg->msg.win.msg == WM_DROPFILES)
2555 if (syswmmsg->msg == WM_DROPFILES)
2558 #if defined(TARGET_SDL2)
2559 HDROP hdrop = (HDROP)syswmmsg->msg.win.wParam;
2561 HDROP hdrop = (HDROP)syswmmsg->wParam;
2565 printf("::: SDL_SYSWMEVENT:\n");
2567 num_files = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
2569 for (i = 0; i < num_files; i++)
2571 int buffer_len = DragQueryFile(hdrop, i, NULL, 0);
2572 char buffer[buffer_len + 1];
2574 DragQueryFile(hdrop, i, buffer, buffer_len + 1);
2576 printf("::: - '%s'\n", buffer);
2579 #if defined(TARGET_SDL2)
2580 DragFinish((HDROP)syswmmsg->msg.win.wParam);
2582 DragFinish((HDROP)syswmmsg->wParam);
2590 /* ========================================================================= */
2591 /* joystick functions */
2592 /* ========================================================================= */
2594 #if defined(TARGET_SDL2)
2595 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2597 static SDL_Joystick *sdl_joystick[MAX_PLAYERS]; // only joysticks supported
2599 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2600 static int sdl_js_axis[MAX_PLAYERS][2];
2601 static int sdl_js_button[MAX_PLAYERS][2];
2602 static boolean sdl_is_controller[MAX_PLAYERS];
2604 void SDLClearJoystickState()
2608 for (i = 0; i < MAX_PLAYERS; i++)
2610 for (j = 0; j < 2; j++)
2612 sdl_js_axis_raw[i][j] = -1;
2613 sdl_js_axis[i][j] = 0;
2614 sdl_js_button[i][j] = 0;
2619 boolean SDLOpenJoystick(int nr)
2621 if (nr < 0 || nr >= MAX_PLAYERS)
2624 #if defined(TARGET_SDL2)
2625 sdl_is_controller[nr] = SDL_IsGameController(nr);
2627 sdl_is_controller[nr] = FALSE;
2631 Error(ERR_DEBUG, "opening joystick %d (%s)",
2632 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2635 #if defined(TARGET_SDL2)
2636 if (sdl_is_controller[nr])
2637 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2639 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2641 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2644 return (sdl_joystick[nr] != NULL);
2647 void SDLCloseJoystick(int nr)
2649 if (nr < 0 || nr >= MAX_PLAYERS)
2653 Error(ERR_DEBUG, "closing joystick %d", nr);
2656 #if defined(TARGET_SDL2)
2657 if (sdl_is_controller[nr])
2658 SDL_GameControllerClose(sdl_joystick[nr]);
2660 SDL_JoystickClose(sdl_joystick[nr]);
2662 SDL_JoystickClose(sdl_joystick[nr]);
2665 sdl_joystick[nr] = NULL;
2668 boolean SDLCheckJoystickOpened(int nr)
2670 if (nr < 0 || nr >= MAX_PLAYERS)
2673 #if defined(TARGET_SDL2)
2674 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2676 return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
2680 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2682 #if defined(TARGET_SDL2)
2683 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2684 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2685 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2686 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2688 int axis_id = axis_id_raw % 2;
2691 if (nr < 0 || nr >= MAX_PLAYERS)
2697 // prevent (slightly jittering, but centered) axis A from resetting axis B
2698 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2699 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2702 sdl_js_axis[nr][axis_id] = axis_value;
2703 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2706 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2708 #if defined(TARGET_SDL2)
2709 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2710 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2711 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2712 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2713 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2714 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2715 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2716 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2719 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2720 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2721 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2722 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2723 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2724 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2725 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2726 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2728 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2729 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2730 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2731 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2732 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2734 int button_id = button_id_raw % 2;
2737 if (nr < 0 || nr >= MAX_PLAYERS)
2740 if (button_id == -1)
2743 sdl_js_button[nr][button_id] = button_state;
2746 void HandleJoystickEvent(Event *event)
2750 #if defined(TARGET_SDL2)
2751 case SDL_CONTROLLERDEVICEADDED:
2753 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2754 event->cdevice.which);
2759 case SDL_CONTROLLERDEVICEREMOVED:
2761 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2762 event->cdevice.which);
2767 case SDL_CONTROLLERAXISMOTION:
2769 Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2770 event->caxis.which, event->caxis.axis, event->caxis.value);
2772 setJoystickAxis(event->caxis.which,
2774 event->caxis.value);
2777 case SDL_CONTROLLERBUTTONDOWN:
2779 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2780 event->cbutton.which, event->cbutton.button);
2782 setJoystickButton(event->cbutton.which,
2783 event->cbutton.button,
2787 case SDL_CONTROLLERBUTTONUP:
2789 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2790 event->cbutton.which, event->cbutton.button);
2792 setJoystickButton(event->cbutton.which,
2793 event->cbutton.button,
2798 case SDL_JOYAXISMOTION:
2799 if (sdl_is_controller[event->jaxis.which])
2803 Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2804 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2806 if (event->jaxis.axis < 4)
2807 setJoystickAxis(event->jaxis.which,
2809 event->jaxis.value);
2812 case SDL_JOYBUTTONDOWN:
2813 if (sdl_is_controller[event->jaxis.which])
2817 Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2818 event->jbutton.which, event->jbutton.button);
2820 if (event->jbutton.button < 4)
2821 setJoystickButton(event->jbutton.which,
2822 event->jbutton.button,
2826 case SDL_JOYBUTTONUP:
2827 if (sdl_is_controller[event->jaxis.which])
2831 Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2832 event->jbutton.which, event->jbutton.button);
2834 if (event->jbutton.button < 4)
2835 setJoystickButton(event->jbutton.which,
2836 event->jbutton.button,
2845 void SDLInitJoysticks()
2847 static boolean sdl_joystick_subsystem_initialized = FALSE;
2848 boolean print_warning = !sdl_joystick_subsystem_initialized;
2849 #if defined(TARGET_SDL2)
2850 char *mappings_file_base = getPath2(options.ro_base_directory,
2851 GAMECONTROLLER_BASENAME);
2852 char *mappings_file_user = getPath2(getUserGameDataDir(),
2853 GAMECONTROLLER_BASENAME);
2858 if (!sdl_joystick_subsystem_initialized)
2860 sdl_joystick_subsystem_initialized = TRUE;
2862 #if defined(TARGET_SDL2)
2863 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2865 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2867 if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
2870 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2874 #if defined(TARGET_SDL2)
2875 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2877 if (num_mappings != -1)
2878 Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2880 Error(ERR_WARN, "no game controller base mappings found");
2882 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2884 if (num_mappings != -1)
2885 Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2887 Error(ERR_WARN, "no game controller user mappings found");
2889 Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2891 checked_free(mappings_file_base);
2892 checked_free(mappings_file_user);
2894 for (i = 0; i < SDL_NumJoysticks(); i++)
2896 const char *name, *type;
2898 if (SDL_IsGameController(i))
2900 name = SDL_GameControllerNameForIndex(i);
2901 type = "game controller";
2905 name = SDL_JoystickNameForIndex(i);
2909 Error(ERR_INFO, "- joystick %d (%s): '%s'",
2910 i, type, (name ? name : "(Unknown)"));
2915 /* assign joysticks from configured to connected joystick for all players */
2916 for (i = 0; i < MAX_PLAYERS; i++)
2918 /* get configured joystick for this player */
2919 char *device_name = setup.input[i].joy.device_name;
2920 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2922 if (joystick_nr >= SDL_NumJoysticks())
2924 if (setup.input[i].use_joystick && print_warning)
2925 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2930 /* store configured joystick number for each player */
2931 joystick.nr[i] = joystick_nr;
2934 /* now open all connected joysticks (regardless if configured or not) */
2935 for (i = 0; i < SDL_NumJoysticks(); i++)
2937 /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
2938 if (SDLCheckJoystickOpened(i))
2939 SDLCloseJoystick(i);
2941 if (SDLOpenJoystick(i))
2942 joystick.status = JOYSTICK_ACTIVATED;
2943 else if (print_warning)
2944 Error(ERR_WARN, "cannot open joystick %d", i);
2947 SDLClearJoystickState();
2950 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2952 if (nr < 0 || nr >= MAX_PLAYERS)
2956 *x = sdl_js_axis[nr][0];
2958 *y = sdl_js_axis[nr][1];
2961 *b1 = sdl_js_button[nr][0];
2963 *b2 = sdl_js_button[nr][1];
2968 #if defined(USE_TOUCH_INPUT_OVERLAY)
2969 static void DrawTouchInputOverlay()
2971 static SDL_Texture *texture = NULL;
2972 static boolean initialized = FALSE;
2973 static boolean deactivated = TRUE;
2974 static int width = 0, height = 0;
2975 static int alpha_max = SDL_ALPHA_OPAQUE / 2;
2976 static int alpha_step = 5;
2977 static int alpha_last = 0;
2978 static int alpha = 0;
2979 boolean active = (overlay.enabled && overlay.active);
2981 if (!active && deactivated)
2986 if (alpha < alpha_max)
2987 alpha = MIN(alpha + alpha_step, alpha_max);
2989 deactivated = FALSE;
2993 alpha = MAX(0, alpha - alpha_step);
3001 char *basename = "overlay/VirtualButtons.png";
3002 char *filename = getCustomImageFilename(basename);
3004 if (filename == NULL)
3005 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
3007 SDL_Surface *surface;
3009 if ((surface = IMG_Load(filename)) == NULL)
3010 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
3013 height = surface->h;
3015 /* set black pixel to transparent if no alpha channel / transparent color */
3016 if (!SDLHasAlpha(surface) &&
3017 !SDLHasColorKey(surface))
3018 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
3019 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
3021 if ((texture = SDLCreateTextureFromSurface(surface)) == NULL)
3022 Error(ERR_EXIT, "SDLCreateTextureFromSurface() failed");
3024 SDL_FreeSurface(surface);
3026 SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
3027 SDL_SetTextureAlphaMod(texture, alpha_max);
3032 if (alpha != alpha_last)
3033 SDL_SetTextureAlphaMod(texture, alpha);
3037 float ratio_overlay = (float) width / height;
3038 float ratio_screen = (float) video.screen_width / video.screen_height;
3039 int width_scaled, height_scaled;
3042 if (ratio_overlay > ratio_screen)
3044 width_scaled = video.screen_width;
3045 height_scaled = video.screen_height * ratio_screen / ratio_overlay;
3047 ypos = video.screen_height - height_scaled;
3051 width_scaled = video.screen_width * ratio_overlay / ratio_screen;
3052 height_scaled = video.screen_height;
3053 xpos = (video.screen_width - width_scaled) / 2;
3057 SDL_Rect src_rect = { 0, 0, width, height };
3058 SDL_Rect dst_rect = { xpos, ypos, width_scaled, height_scaled };
3060 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);