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 */
538 inline static void SDLInitVideoBuffer_DrawBuffer()
540 /* SDL cannot directly draw to the visible video framebuffer like X11,
541 but always uses a backbuffer, which is then blitted to the visible
542 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
543 visible video framebuffer with 'SDL_Flip', if the hardware supports
544 this). Therefore do not use an additional backbuffer for drawing, but
545 use a symbolic buffer (distinguishable from the SDL backbuffer) called
546 'window', which indicates that the SDL backbuffer should be updated to
547 the visible video framebuffer when attempting to blit to it.
549 For convenience, it seems to be a good idea to create this symbolic
550 buffer 'window' at the same size as the SDL backbuffer. Although it
551 should never be drawn to directly, it would do no harm nevertheless. */
553 /* create additional (symbolic) buffer for double-buffering */
554 ReCreateBitmap(&window, video.width, video.height);
556 /* create dummy drawing buffer for headless mode, if needed */
557 if (program.headless)
558 ReCreateBitmap(&backbuffer, video.width, video.height);
561 void SDLInitVideoBuffer(boolean fullscreen)
563 SDLInitVideoBuffer_VideoBuffer(fullscreen);
564 SDLInitVideoBuffer_DrawBuffer();
567 static boolean SDLCreateScreen(boolean fullscreen)
569 SDL_Surface *new_surface = NULL;
571 #if defined(TARGET_SDL2)
572 int surface_flags_window = SURFACE_FLAGS | SDL_WINDOW_RESIZABLE;
573 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
575 int surface_flags_window = SURFACE_FLAGS;
576 int surface_flags_fullscreen = SURFACE_FLAGS; // (no fullscreen in SDL 1.2)
579 #if defined(TARGET_SDL2)
581 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
583 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
584 _without_ enabling 2D/3D acceleration and/or guest additions installed,
585 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
586 it will try to use accelerated graphics and apparently fails miserably) */
587 int renderer_flags = SDL_RENDERER_SOFTWARE;
590 SDLSetScreenSizeAndOffsets(video.width, video.height);
593 int width = video.width;
594 int height = video.height;
595 int screen_width = video.screen_width;
596 int screen_height = video.screen_height;
597 int surface_flags = (fullscreen ? surface_flags_fullscreen :
598 surface_flags_window);
600 // default window size is unscaled
601 video.window_width = screen_width;
602 video.window_height = screen_height;
604 #if defined(TARGET_SDL2)
606 // store if initial screen mode is fullscreen mode when changing screen size
607 video.fullscreen_initial = fullscreen;
609 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
611 video.window_width = window_scaling_factor * screen_width;
612 video.window_height = window_scaling_factor * screen_height;
614 if (sdl_texture_stream)
616 SDL_DestroyTexture(sdl_texture_stream);
617 sdl_texture_stream = NULL;
620 if (sdl_texture_target)
622 SDL_DestroyTexture(sdl_texture_target);
623 sdl_texture_target = NULL;
626 if (!(fullscreen && fullscreen_enabled))
630 SDL_DestroyRenderer(sdl_renderer);
636 SDL_DestroyWindow(sdl_window);
641 if (sdl_window == NULL)
642 sdl_window = SDL_CreateWindow(program.window_title,
643 SDL_WINDOWPOS_CENTERED,
644 SDL_WINDOWPOS_CENTERED,
649 if (sdl_window != NULL)
651 if (sdl_renderer == NULL)
652 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
654 if (sdl_renderer != NULL)
656 SDL_RenderSetLogicalSize(sdl_renderer, screen_width, screen_height);
657 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
658 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
660 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
661 SDL_PIXELFORMAT_ARGB8888,
662 SDL_TEXTUREACCESS_STREAMING,
665 if (SDL_RenderTargetSupported(sdl_renderer))
666 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
667 SDL_PIXELFORMAT_ARGB8888,
668 SDL_TEXTUREACCESS_TARGET,
671 if (sdl_texture_stream != NULL)
673 // use SDL default values for RGB masks and no alpha channel
674 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
676 if (new_surface == NULL)
677 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
681 Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
686 Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
691 Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
696 if (gfx.final_screen_bitmap == NULL)
697 gfx.final_screen_bitmap = CreateBitmapStruct();
699 gfx.final_screen_bitmap->width = width;
700 gfx.final_screen_bitmap->height = height;
702 gfx.final_screen_bitmap->surface =
703 SDL_SetVideoMode(width, height, video.depth, surface_flags);
705 if (gfx.final_screen_bitmap->surface != NULL)
708 SDL_CreateRGBSurface(surface_flags, width, height, video.depth, 0,0,0, 0);
710 if (new_surface == NULL)
711 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
714 new_surface = gfx.final_screen_bitmap->surface;
715 gfx.final_screen_bitmap = NULL;
721 Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
725 #if defined(TARGET_SDL2)
726 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
727 if (new_surface != NULL)
728 fullscreen_enabled = fullscreen;
731 if (backbuffer == NULL)
732 backbuffer = CreateBitmapStruct();
734 backbuffer->width = video.width;
735 backbuffer->height = video.height;
737 if (backbuffer->surface)
738 SDL_FreeSurface(backbuffer->surface);
740 backbuffer->surface = new_surface;
742 return (new_surface != NULL);
745 boolean SDLSetVideoMode(boolean fullscreen)
747 boolean success = FALSE;
751 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
753 /* switch display to fullscreen mode, if available */
754 success = SDLCreateScreen(TRUE);
758 /* switching display to fullscreen mode failed -- do not try it again */
759 video.fullscreen_available = FALSE;
763 video.fullscreen_enabled = TRUE;
767 if ((!fullscreen && video.fullscreen_enabled) || !success)
769 /* switch display to window mode */
770 success = SDLCreateScreen(FALSE);
774 /* switching display to window mode failed -- should not happen */
778 video.fullscreen_enabled = FALSE;
779 video.window_scaling_percent = setup.window_scaling_percent;
780 video.window_scaling_quality = setup.window_scaling_quality;
782 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
786 #if defined(TARGET_SDL2)
787 SDLRedrawWindow(); // map window
791 #if defined(PLATFORM_WIN32)
792 // experimental drag and drop code
794 SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
797 SDL_SysWMinfo wminfo;
799 boolean wminfo_success = FALSE;
801 SDL_VERSION(&wminfo.version);
802 #if defined(TARGET_SDL2)
804 wminfo_success = SDL_GetWindowWMInfo(sdl_window, &wminfo);
806 wminfo_success = (SDL_GetWMInfo(&wminfo) == 1);
811 #if defined(TARGET_SDL2)
812 hwnd = wminfo.info.win.window;
814 hwnd = wminfo.window;
817 DragAcceptFiles(hwnd, TRUE);
826 void SDLSetWindowTitle()
828 #if defined(TARGET_SDL2)
829 if (sdl_window == NULL)
832 SDL_SetWindowTitle(sdl_window, program.window_title);
834 SDL_WM_SetCaption(program.window_title, program.window_title);
838 #if defined(TARGET_SDL2)
839 void SDLSetWindowScaling(int window_scaling_percent)
841 if (sdl_window == NULL)
844 float window_scaling_factor = (float)window_scaling_percent / 100;
845 int new_window_width = (int)(window_scaling_factor * video.screen_width);
846 int new_window_height = (int)(window_scaling_factor * video.screen_height);
848 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
850 video.window_scaling_percent = window_scaling_percent;
851 video.window_width = new_window_width;
852 video.window_height = new_window_height;
857 void SDLSetWindowScalingQuality(char *window_scaling_quality)
859 SDL_Texture *new_texture;
861 if (sdl_texture_stream == NULL)
864 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
866 new_texture = SDL_CreateTexture(sdl_renderer,
867 SDL_PIXELFORMAT_ARGB8888,
868 SDL_TEXTUREACCESS_STREAMING,
869 video.width, video.height);
871 if (new_texture != NULL)
873 SDL_DestroyTexture(sdl_texture_stream);
875 sdl_texture_stream = new_texture;
878 if (SDL_RenderTargetSupported(sdl_renderer))
879 new_texture = SDL_CreateTexture(sdl_renderer,
880 SDL_PIXELFORMAT_ARGB8888,
881 SDL_TEXTUREACCESS_TARGET,
882 video.width, video.height);
886 if (new_texture != NULL)
888 SDL_DestroyTexture(sdl_texture_target);
890 sdl_texture_target = new_texture;
895 video.window_scaling_quality = window_scaling_quality;
898 void SDLSetWindowFullscreen(boolean fullscreen)
900 if (sdl_window == NULL)
903 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
905 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
906 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
908 // if screen size was changed in fullscreen mode, correct desktop window size
909 if (!fullscreen && video.fullscreen_initial)
911 SDLSetWindowScaling(setup.window_scaling_percent);
912 SDL_SetWindowPosition(sdl_window,
913 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
915 video.fullscreen_initial = FALSE;
919 void SDLSetDisplaySize()
921 SDL_Rect display_bounds;
923 SDL_GetDisplayBounds(0, &display_bounds);
925 video.display_width = display_bounds.w;
926 video.display_height = display_bounds.h;
929 Error(ERR_DEBUG, "SDL real screen size: %d x %d",
930 video.display_width, video.display_height);
934 void SDLSetScreenSizeAndOffsets(int width, int height)
936 // set default video screen size and offsets
937 video.screen_width = width;
938 video.screen_height = height;
939 video.screen_xoffset = 0;
940 video.screen_yoffset = 0;
942 #if defined(USE_COMPLETE_DISPLAY)
943 float ratio_video = (float) width / height;
944 float ratio_display = (float) video.display_width / video.display_height;
946 if (ratio_video != ratio_display)
948 // adjust drawable screen size to cover the whole device display
950 if (ratio_video < ratio_display)
951 video.screen_width *= ratio_display / ratio_video;
953 video.screen_height *= ratio_video / ratio_display;
955 video.screen_xoffset = (video.screen_width - width) / 2;
956 video.screen_yoffset = (video.screen_height - height) / 2;
959 Error(ERR_DEBUG, "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
961 video.screen_width, video.screen_height,
962 ratio_video, ratio_display);
968 void SDLSetScreenSizeForRenderer(int width, int height)
970 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
973 void SDLSetScreenProperties()
975 SDLSetScreenSizeAndOffsets(video.width, video.height);
976 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
981 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
983 #if defined(TARGET_SDL2)
984 video.screen_rendering_mode =
985 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
986 SPECIAL_RENDERING_BITMAP :
987 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
988 SPECIAL_RENDERING_TARGET:
989 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
990 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
992 video.screen_rendering_mode = SPECIAL_RENDERING_BITMAP;
996 void SDLRedrawWindow()
998 UpdateScreen_WithoutFrameDelay(NULL);
1001 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
1004 if (program.headless)
1007 SDL_Surface *surface =
1008 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
1010 if (surface == NULL)
1011 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
1013 SDLSetNativeSurface(&surface);
1015 bitmap->surface = surface;
1018 void SDLFreeBitmapPointers(Bitmap *bitmap)
1020 if (bitmap->surface)
1021 SDL_FreeSurface(bitmap->surface);
1022 if (bitmap->surface_masked)
1023 SDL_FreeSurface(bitmap->surface_masked);
1025 bitmap->surface = NULL;
1026 bitmap->surface_masked = NULL;
1028 #if defined(TARGET_SDL2)
1029 if (bitmap->texture)
1030 SDL_DestroyTexture(bitmap->texture);
1031 if (bitmap->texture_masked)
1032 SDL_DestroyTexture(bitmap->texture_masked);
1034 bitmap->texture = NULL;
1035 bitmap->texture_masked = NULL;
1039 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1040 int src_x, int src_y, int width, int height,
1041 int dst_x, int dst_y, int mask_mode)
1043 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1044 SDL_Rect src_rect, dst_rect;
1049 src_rect.h = height;
1054 dst_rect.h = height;
1056 // if (src_bitmap != backbuffer || dst_bitmap != window)
1057 if (!(src_bitmap == backbuffer && dst_bitmap == window))
1058 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
1059 src_bitmap->surface_masked : src_bitmap->surface),
1060 &src_rect, real_dst_bitmap->surface, &dst_rect);
1062 if (dst_bitmap == window)
1063 UpdateScreen_WithFrameDelay(&dst_rect);
1066 void SDLBlitTexture(Bitmap *bitmap,
1067 int src_x, int src_y, int width, int height,
1068 int dst_x, int dst_y, int mask_mode)
1070 #if defined(TARGET_SDL2)
1071 SDL_Texture *texture;
1076 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1078 if (texture == NULL)
1084 src_rect.h = height;
1089 dst_rect.h = height;
1091 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1095 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1098 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1106 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1108 if (dst_bitmap == window)
1109 UpdateScreen_WithFrameDelay(&rect);
1112 void PrepareFadeBitmap(int draw_target)
1114 Bitmap *fade_bitmap =
1115 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1116 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1118 if (fade_bitmap == NULL)
1121 // copy backbuffer to fading buffer
1122 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1124 // add border and animations to fading buffer
1125 FinalizeScreen(draw_target);
1128 void SDLFadeRectangle(int x, int y, int width, int height,
1129 int fade_mode, int fade_delay, int post_delay,
1130 void (*draw_border_function)(void))
1132 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1133 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1134 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1135 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1136 SDL_Surface *surface_screen = backbuffer->surface;
1137 SDL_Rect src_rect, dst_rect;
1139 int src_x = x, src_y = y;
1140 int dst_x = x, dst_y = y;
1141 unsigned int time_last, time_current;
1143 // store function for drawing global masked border
1144 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1146 // deactivate drawing of global border while fading, if needed
1147 if (draw_border_function == NULL)
1148 gfx.draw_global_border_function = NULL;
1153 src_rect.h = height;
1157 dst_rect.w = width; /* (ignored) */
1158 dst_rect.h = height; /* (ignored) */
1160 dst_rect2 = dst_rect;
1162 // before fading in, store backbuffer (without animation graphics)
1163 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1164 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1166 /* copy source and target surfaces to temporary surfaces for fading */
1167 if (fade_mode & FADE_TYPE_TRANSFORM)
1169 // (source and target fading buffer already prepared)
1171 else if (fade_mode & FADE_TYPE_FADE_IN)
1173 // (target fading buffer already prepared)
1174 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1176 else /* FADE_TYPE_FADE_OUT */
1178 // (source fading buffer already prepared)
1179 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1182 time_current = SDL_GetTicks();
1184 if (fade_mode == FADE_MODE_MELT)
1186 boolean done = FALSE;
1187 int melt_pixels = 2;
1188 int melt_columns = width / melt_pixels;
1189 int ypos[melt_columns];
1190 int max_steps = height / 8 + 32;
1195 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1197 SDLSetAlpha(surface_target, FALSE, 0); /* disable alpha blending */
1199 ypos[0] = -GetSimpleRandom(16);
1201 for (i = 1 ; i < melt_columns; i++)
1203 int r = GetSimpleRandom(3) - 1; /* randomly choose from { -1, 0, -1 } */
1205 ypos[i] = ypos[i - 1] + r;
1218 time_last = time_current;
1219 time_current = SDL_GetTicks();
1220 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1221 steps_final = MIN(MAX(0, steps), max_steps);
1225 done = (steps_done >= steps_final);
1227 for (i = 0 ; i < melt_columns; i++)
1235 else if (ypos[i] < height)
1240 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1242 if (ypos[i] + dy >= height)
1243 dy = height - ypos[i];
1245 /* copy part of (appearing) target surface to upper area */
1246 src_rect.x = src_x + i * melt_pixels;
1247 // src_rect.y = src_y + ypos[i];
1249 src_rect.w = melt_pixels;
1251 src_rect.h = ypos[i] + dy;
1253 dst_rect.x = dst_x + i * melt_pixels;
1254 // dst_rect.y = dst_y + ypos[i];
1257 if (steps_done >= steps_final)
1258 SDL_BlitSurface(surface_target, &src_rect,
1259 surface_screen, &dst_rect);
1263 /* copy part of (disappearing) source surface to lower area */
1264 src_rect.x = src_x + i * melt_pixels;
1266 src_rect.w = melt_pixels;
1267 src_rect.h = height - ypos[i];
1269 dst_rect.x = dst_x + i * melt_pixels;
1270 dst_rect.y = dst_y + ypos[i];
1272 if (steps_done >= steps_final)
1273 SDL_BlitSurface(surface_source, &src_rect,
1274 surface_screen, &dst_rect);
1280 src_rect.x = src_x + i * melt_pixels;
1282 src_rect.w = melt_pixels;
1283 src_rect.h = height;
1285 dst_rect.x = dst_x + i * melt_pixels;
1288 if (steps_done >= steps_final)
1289 SDL_BlitSurface(surface_target, &src_rect,
1290 surface_screen, &dst_rect);
1294 if (steps_done >= steps_final)
1296 if (draw_border_function != NULL)
1297 draw_border_function();
1299 UpdateScreen_WithFrameDelay(&dst_rect2);
1303 else if (fade_mode == FADE_MODE_CURTAIN)
1307 int xx_size = width / 2;
1309 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1311 SDLSetAlpha(surface_source, FALSE, 0); /* disable alpha blending */
1313 for (xx = 0; xx < xx_size;)
1315 time_last = time_current;
1316 time_current = SDL_GetTicks();
1317 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1318 xx_final = MIN(MAX(0, xx), xx_size);
1323 src_rect.h = height;
1328 /* draw new (target) image to screen buffer */
1329 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1331 if (xx_final < xx_size)
1333 src_rect.w = xx_size - xx_final;
1334 src_rect.h = height;
1336 /* draw old (source) image to screen buffer (left side) */
1338 src_rect.x = src_x + xx_final;
1341 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1343 /* draw old (source) image to screen buffer (right side) */
1345 src_rect.x = src_x + xx_size;
1346 dst_rect.x = dst_x + xx_size + xx_final;
1348 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1351 if (draw_border_function != NULL)
1352 draw_border_function();
1354 /* only update the region of the screen that is affected from fading */
1355 UpdateScreen_WithFrameDelay(&dst_rect2);
1358 else /* fading in, fading out or cross-fading */
1363 for (alpha = 0.0; alpha < 255.0;)
1365 time_last = time_current;
1366 time_current = SDL_GetTicks();
1367 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1368 alpha_final = MIN(MAX(0, alpha), 255);
1370 /* draw existing (source) image to screen buffer */
1371 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1373 /* draw new (target) image to screen buffer using alpha blending */
1374 SDLSetAlpha(surface_target, TRUE, alpha_final);
1375 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1377 if (draw_border_function != NULL)
1378 draw_border_function();
1380 /* only update the region of the screen that is affected from fading */
1381 UpdateScreen_WithFrameDelay(&dst_rect);
1387 unsigned int time_post_delay;
1389 time_current = SDL_GetTicks();
1390 time_post_delay = time_current + post_delay;
1392 while (time_current < time_post_delay)
1394 // updating the screen contains waiting for frame delay (non-busy)
1395 UpdateScreen_WithFrameDelay(NULL);
1397 time_current = SDL_GetTicks();
1401 // restore function for drawing global masked border
1402 gfx.draw_global_border_function = draw_global_border_function;
1404 // after fading in, restore backbuffer (without animation graphics)
1405 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1406 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1409 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1410 int to_x, int to_y, Uint32 color)
1412 SDL_Surface *surface = dst_bitmap->surface;
1416 swap_numbers(&from_x, &to_x);
1419 swap_numbers(&from_y, &to_y);
1423 rect.w = (to_x - from_x + 1);
1424 rect.h = (to_y - from_y + 1);
1426 SDL_FillRect(surface, &rect, color);
1429 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1430 int to_x, int to_y, Uint32 color)
1432 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1435 #if ENABLE_UNUSED_CODE
1436 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1437 int num_points, Uint32 color)
1442 for (i = 0; i < num_points - 1; i++)
1444 for (x = 0; x < line_width; x++)
1446 for (y = 0; y < line_width; y++)
1448 int dx = x - line_width / 2;
1449 int dy = y - line_width / 2;
1451 if ((x == 0 && y == 0) ||
1452 (x == 0 && y == line_width - 1) ||
1453 (x == line_width - 1 && y == 0) ||
1454 (x == line_width - 1 && y == line_width - 1))
1457 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1458 points[i+1].x + dx, points[i+1].y + dy, color);
1465 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1467 SDL_Surface *surface = src_bitmap->surface;
1469 switch (surface->format->BytesPerPixel)
1471 case 1: /* assuming 8-bpp */
1473 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1477 case 2: /* probably 15-bpp or 16-bpp */
1479 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1483 case 3: /* slow 24-bpp mode; usually not used */
1485 /* does this work? */
1486 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1490 shift = surface->format->Rshift;
1491 color |= *(pix + shift / 8) >> shift;
1492 shift = surface->format->Gshift;
1493 color |= *(pix + shift / 8) >> shift;
1494 shift = surface->format->Bshift;
1495 color |= *(pix + shift / 8) >> shift;
1501 case 4: /* probably 32-bpp */
1503 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1512 /* ========================================================================= */
1513 /* The following functions were taken from the SGE library */
1514 /* (SDL Graphics Extension Library) by Anders Lindström */
1515 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html */
1516 /* ========================================================================= */
1518 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1520 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1522 switch (surface->format->BytesPerPixel)
1526 /* Assuming 8-bpp */
1527 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1533 /* Probably 15-bpp or 16-bpp */
1534 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1540 /* Slow 24-bpp mode, usually not used */
1544 /* Gack - slow, but endian correct */
1545 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1546 shift = surface->format->Rshift;
1547 *(pix+shift/8) = color>>shift;
1548 shift = surface->format->Gshift;
1549 *(pix+shift/8) = color>>shift;
1550 shift = surface->format->Bshift;
1551 *(pix+shift/8) = color>>shift;
1557 /* Probably 32-bpp */
1558 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1565 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1566 Uint8 R, Uint8 G, Uint8 B)
1568 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1571 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1573 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1576 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1578 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1581 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1586 /* Gack - slow, but endian correct */
1587 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1588 shift = surface->format->Rshift;
1589 *(pix+shift/8) = color>>shift;
1590 shift = surface->format->Gshift;
1591 *(pix+shift/8) = color>>shift;
1592 shift = surface->format->Bshift;
1593 *(pix+shift/8) = color>>shift;
1596 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1598 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1601 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1603 switch (dest->format->BytesPerPixel)
1606 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1610 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1614 _PutPixel24(dest,x,y,color);
1618 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1623 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1625 if (SDL_MUSTLOCK(surface))
1627 if (SDL_LockSurface(surface) < 0)
1633 _PutPixel(surface, x, y, color);
1635 if (SDL_MUSTLOCK(surface))
1637 SDL_UnlockSurface(surface);
1641 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1642 Uint8 r, Uint8 g, Uint8 b)
1644 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1647 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1649 if (y >= 0 && y <= dest->h - 1)
1651 switch (dest->format->BytesPerPixel)
1654 return y*dest->pitch;
1658 return y*dest->pitch/2;
1662 return y*dest->pitch;
1666 return y*dest->pitch/4;
1674 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
1676 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1678 switch (surface->format->BytesPerPixel)
1682 /* Assuming 8-bpp */
1683 *((Uint8 *)surface->pixels + ypitch + x) = color;
1689 /* Probably 15-bpp or 16-bpp */
1690 *((Uint16 *)surface->pixels + ypitch + x) = color;
1696 /* Slow 24-bpp mode, usually not used */
1700 /* Gack - slow, but endian correct */
1701 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1702 shift = surface->format->Rshift;
1703 *(pix+shift/8) = color>>shift;
1704 shift = surface->format->Gshift;
1705 *(pix+shift/8) = color>>shift;
1706 shift = surface->format->Bshift;
1707 *(pix+shift/8) = color>>shift;
1713 /* Probably 32-bpp */
1714 *((Uint32 *)surface->pixels + ypitch + x) = color;
1721 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1726 if (SDL_MUSTLOCK(Surface))
1728 if (SDL_LockSurface(Surface) < 0)
1741 /* Do the clipping */
1742 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1746 if (x2 > Surface->w - 1)
1747 x2 = Surface->w - 1;
1754 SDL_FillRect(Surface, &l, Color);
1756 if (SDL_MUSTLOCK(Surface))
1758 SDL_UnlockSurface(Surface);
1762 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1763 Uint8 R, Uint8 G, Uint8 B)
1765 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1768 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
1779 /* Do the clipping */
1780 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1784 if (x2 > Surface->w - 1)
1785 x2 = Surface->w - 1;
1792 SDL_FillRect(Surface, &l, Color);
1795 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1800 if (SDL_MUSTLOCK(Surface))
1802 if (SDL_LockSurface(Surface) < 0)
1815 /* Do the clipping */
1816 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1820 if (y2 > Surface->h - 1)
1821 y2 = Surface->h - 1;
1828 SDL_FillRect(Surface, &l, Color);
1830 if (SDL_MUSTLOCK(Surface))
1832 SDL_UnlockSurface(Surface);
1836 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1837 Uint8 R, Uint8 G, Uint8 B)
1839 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1842 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
1853 /* Do the clipping */
1854 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1858 if (y2 > Surface->h - 1)
1859 y2 = Surface->h - 1;
1866 SDL_FillRect(Surface, &l, Color);
1869 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1870 Sint16 x2, Sint16 y2, Uint32 Color,
1871 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1874 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1879 sdx = (dx < 0) ? -1 : 1;
1880 sdy = (dy < 0) ? -1 : 1;
1892 for (x = 0; x < dx; x++)
1894 Callback(Surface, px, py, Color);
1908 for (y = 0; y < dy; y++)
1910 Callback(Surface, px, py, Color);
1924 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1925 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1926 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1929 sge_DoLine(Surface, X1, Y1, X2, Y2,
1930 SDL_MapRGB(Surface->format, R, G, B), Callback);
1933 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1936 if (SDL_MUSTLOCK(Surface))
1938 if (SDL_LockSurface(Surface) < 0)
1943 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1945 /* unlock the display */
1946 if (SDL_MUSTLOCK(Surface))
1948 SDL_UnlockSurface(Surface);
1952 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1953 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1955 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1958 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1960 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1965 -----------------------------------------------------------------------------
1966 quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1967 -----------------------------------------------------------------------------
1970 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1971 int width, int height, Uint32 color)
1975 for (y = src_y; y < src_y + height; y++)
1977 for (x = src_x; x < src_x + width; x++)
1979 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1981 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1986 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1987 int src_x, int src_y, int width, int height,
1988 int dst_x, int dst_y)
1992 for (y = 0; y < height; y++)
1994 for (x = 0; x < width; x++)
1996 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1998 if (pixel != BLACK_PIXEL)
1999 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
2005 /* ========================================================================= */
2006 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
2007 /* (Rotozoomer) by Andreas Schiffler */
2008 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html */
2009 /* ========================================================================= */
2012 -----------------------------------------------------------------------------
2015 zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
2016 -----------------------------------------------------------------------------
2027 int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
2030 tColorRGBA *sp, *csp, *dp;
2034 sp = csp = (tColorRGBA *) src->pixels;
2035 dp = (tColorRGBA *) dst->pixels;
2036 dgap = dst->pitch - dst->w * 4;
2038 for (y = 0; y < dst->h; y++)
2042 for (x = 0; x < dst->w; x++)
2044 tColorRGBA *sp0 = sp;
2045 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
2046 tColorRGBA *sp00 = &sp0[0];
2047 tColorRGBA *sp01 = &sp0[1];
2048 tColorRGBA *sp10 = &sp1[0];
2049 tColorRGBA *sp11 = &sp1[1];
2052 /* create new color pixel from all four source color pixels */
2053 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
2054 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
2055 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
2056 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
2061 /* advance source pointers */
2064 /* advance destination pointer */
2068 /* advance source pointer */
2069 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2071 /* advance destination pointers */
2072 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2078 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2080 int x, y, *sax, *say, *csax, *csay;
2082 tColorRGBA *sp, *csp, *csp0, *dp;
2085 /* use specialized zoom function when scaling down to exactly half size */
2086 if (src->w == 2 * dst->w &&
2087 src->h == 2 * dst->h)
2088 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2090 /* variable setup */
2091 sx = (float) src->w / (float) dst->w;
2092 sy = (float) src->h / (float) dst->h;
2094 /* allocate memory for row increments */
2095 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2096 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2098 /* precalculate row increments */
2099 for (x = 0; x <= dst->w; x++)
2100 *csax++ = (int)(sx * x);
2102 for (y = 0; y <= dst->h; y++)
2103 *csay++ = (int)(sy * y);
2106 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2107 dp = (tColorRGBA *) dst->pixels;
2108 dgap = dst->pitch - dst->w * 4;
2111 for (y = 0; y < dst->h; y++)
2116 for (x = 0; x < dst->w; x++)
2121 /* advance source pointers */
2125 /* advance destination pointer */
2129 /* advance source pointer */
2131 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2133 /* advance destination pointers */
2134 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2144 -----------------------------------------------------------------------------
2147 zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2148 -----------------------------------------------------------------------------
2151 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2153 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2154 Uint8 *sp, *dp, *csp;
2157 /* variable setup */
2158 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2159 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2161 /* allocate memory for row increments */
2162 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2163 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2165 /* precalculate row increments */
2168 for (x = 0; x < dst->w; x++)
2171 *csax = (csx >> 16);
2178 for (y = 0; y < dst->h; y++)
2181 *csay = (csy >> 16);
2188 for (x = 0; x < dst->w; x++)
2196 for (y = 0; y < dst->h; y++)
2203 sp = csp = (Uint8 *) src->pixels;
2204 dp = (Uint8 *) dst->pixels;
2205 dgap = dst->pitch - dst->w;
2209 for (y = 0; y < dst->h; y++)
2213 for (x = 0; x < dst->w; x++)
2218 /* advance source pointers */
2222 /* advance destination pointer */
2226 /* advance source pointer (for row) */
2227 csp += ((*csay) * src->pitch);
2230 /* advance destination pointers */
2241 -----------------------------------------------------------------------------
2244 Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2245 'zoomx' and 'zoomy' are scaling factors for width and height.
2246 If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2247 into a 32bit RGBA format on the fly.
2248 -----------------------------------------------------------------------------
2251 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2253 SDL_Surface *zoom_src = NULL;
2254 SDL_Surface *zoom_dst = NULL;
2255 boolean is_converted = FALSE;
2262 /* determine if source surface is 32 bit or 8 bit */
2263 is_32bit = (src->format->BitsPerPixel == 32);
2265 if (is_32bit || src->format->BitsPerPixel == 8)
2267 /* use source surface 'as is' */
2272 /* new source surface is 32 bit with a defined RGB ordering */
2273 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2274 0x000000ff, 0x0000ff00, 0x00ff0000,
2275 (src->format->Amask ? 0xff000000 : 0));
2276 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2278 is_converted = TRUE;
2281 /* allocate surface to completely contain the zoomed surface */
2284 /* target surface is 32 bit with source RGBA/ABGR ordering */
2285 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2286 zoom_src->format->Rmask,
2287 zoom_src->format->Gmask,
2288 zoom_src->format->Bmask,
2289 zoom_src->format->Amask);
2293 /* target surface is 8 bit */
2294 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2298 /* lock source surface */
2299 SDL_LockSurface(zoom_src);
2301 /* check which kind of surface we have */
2304 /* call the 32 bit transformation routine to do the zooming */
2305 zoomSurfaceRGBA(zoom_src, zoom_dst);
2310 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2311 zoom_dst->format->palette->colors[i] =
2312 zoom_src->format->palette->colors[i];
2313 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2315 /* call the 8 bit transformation routine to do the zooming */
2316 zoomSurfaceY(zoom_src, zoom_dst);
2319 /* unlock source surface */
2320 SDL_UnlockSurface(zoom_src);
2322 /* free temporary surface */
2324 SDL_FreeSurface(zoom_src);
2326 /* return destination surface */
2330 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2332 SDL_Surface *new_surface;
2334 if (surface == NULL)
2337 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2338 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2340 /* remove alpha channel from native non-transparent surface, if defined */
2341 SDLSetAlpha(new_surface, FALSE, 0);
2343 /* remove transparent color from native non-transparent surface, if defined */
2344 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2349 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2351 Bitmap *dst_bitmap = CreateBitmapStruct();
2352 SDL_Surface *src_surface = src_bitmap->surface_masked;
2353 SDL_Surface *dst_surface;
2355 dst_width = MAX(1, dst_width); /* prevent zero bitmap width */
2356 dst_height = MAX(1, dst_height); /* prevent zero bitmap height */
2358 dst_bitmap->width = dst_width;
2359 dst_bitmap->height = dst_height;
2361 /* create zoomed temporary surface from source surface */
2362 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2364 /* create native format destination surface from zoomed temporary surface */
2365 SDLSetNativeSurface(&dst_surface);
2367 /* set color key for zoomed surface from source surface, if defined */
2368 if (SDLHasColorKey(src_surface))
2369 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2370 SDLGetColorKey(src_surface));
2372 /* create native non-transparent surface for opaque blitting */
2373 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2375 /* set native transparent surface for masked blitting */
2376 dst_bitmap->surface_masked = dst_surface;
2382 /* ========================================================================= */
2383 /* load image to bitmap */
2384 /* ========================================================================= */
2386 Bitmap *SDLLoadImage(char *filename)
2388 Bitmap *new_bitmap = CreateBitmapStruct();
2389 SDL_Surface *sdl_image_tmp;
2391 if (program.headless)
2393 /* prevent sanity check warnings at later stage */
2394 new_bitmap->width = new_bitmap->height = 1;
2399 print_timestamp_init("SDLLoadImage");
2401 print_timestamp_time(getBaseNamePtr(filename));
2403 /* load image to temporary surface */
2404 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2405 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
2407 print_timestamp_time("IMG_Load");
2409 UPDATE_BUSY_STATE();
2411 /* create native non-transparent surface for current image */
2412 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2413 Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2415 print_timestamp_time("SDLGetNativeSurface (opaque)");
2417 UPDATE_BUSY_STATE();
2419 /* set black pixel to transparent if no alpha channel / transparent color */
2420 if (!SDLHasAlpha(sdl_image_tmp) &&
2421 !SDLHasColorKey(sdl_image_tmp))
2422 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2423 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2425 /* create native transparent surface for current image */
2426 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2427 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2429 print_timestamp_time("SDLGetNativeSurface (masked)");
2431 UPDATE_BUSY_STATE();
2433 /* free temporary surface */
2434 SDL_FreeSurface(sdl_image_tmp);
2436 new_bitmap->width = new_bitmap->surface->w;
2437 new_bitmap->height = new_bitmap->surface->h;
2439 print_timestamp_done("SDLLoadImage");
2445 /* ------------------------------------------------------------------------- */
2446 /* custom cursor fuctions */
2447 /* ------------------------------------------------------------------------- */
2449 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2451 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2452 cursor_info->width, cursor_info->height,
2453 cursor_info->hot_x, cursor_info->hot_y);
2456 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2458 static struct MouseCursorInfo *last_cursor_info = NULL;
2459 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2460 static SDL_Cursor *cursor_default = NULL;
2461 static SDL_Cursor *cursor_current = NULL;
2463 /* if invoked for the first time, store the SDL default cursor */
2464 if (cursor_default == NULL)
2465 cursor_default = SDL_GetCursor();
2467 /* only create new cursor if cursor info (custom only) has changed */
2468 if (cursor_info != NULL && cursor_info != last_cursor_info)
2470 cursor_current = create_cursor(cursor_info);
2471 last_cursor_info = cursor_info;
2474 /* only set new cursor if cursor info (custom or NULL) has changed */
2475 if (cursor_info != last_cursor_info2)
2476 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2478 last_cursor_info2 = cursor_info;
2482 /* ========================================================================= */
2483 /* audio functions */
2484 /* ========================================================================= */
2486 void SDLOpenAudio(void)
2488 if (program.headless)
2491 #if !defined(TARGET_SDL2)
2492 if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
2493 SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
2496 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2498 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2502 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2503 AUDIO_NUM_CHANNELS_STEREO,
2504 setup.system.audio_fragment_size) < 0)
2506 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2510 audio.sound_available = TRUE;
2511 audio.music_available = TRUE;
2512 audio.loops_available = TRUE;
2513 audio.sound_enabled = TRUE;
2515 /* set number of available mixer channels */
2516 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2517 audio.music_channel = MUSIC_CHANNEL;
2518 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2520 Mixer_InitChannels();
2523 void SDLCloseAudio(void)
2526 Mix_HaltChannel(-1);
2529 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2533 /* ========================================================================= */
2534 /* event functions */
2535 /* ========================================================================= */
2537 void SDLNextEvent(Event *event)
2539 SDL_WaitEvent(event);
2542 void SDLHandleWindowManagerEvent(Event *event)
2545 #if defined(PLATFORM_WIN32)
2546 // experimental drag and drop code
2548 SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
2549 SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
2551 #if defined(TARGET_SDL2)
2552 if (syswmmsg->msg.win.msg == WM_DROPFILES)
2554 if (syswmmsg->msg == WM_DROPFILES)
2557 #if defined(TARGET_SDL2)
2558 HDROP hdrop = (HDROP)syswmmsg->msg.win.wParam;
2560 HDROP hdrop = (HDROP)syswmmsg->wParam;
2564 printf("::: SDL_SYSWMEVENT:\n");
2566 num_files = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
2568 for (i = 0; i < num_files; i++)
2570 int buffer_len = DragQueryFile(hdrop, i, NULL, 0);
2571 char buffer[buffer_len + 1];
2573 DragQueryFile(hdrop, i, buffer, buffer_len + 1);
2575 printf("::: - '%s'\n", buffer);
2578 #if defined(TARGET_SDL2)
2579 DragFinish((HDROP)syswmmsg->msg.win.wParam);
2581 DragFinish((HDROP)syswmmsg->wParam);
2589 /* ========================================================================= */
2590 /* joystick functions */
2591 /* ========================================================================= */
2593 #if defined(TARGET_SDL2)
2594 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2596 static SDL_Joystick *sdl_joystick[MAX_PLAYERS]; // only joysticks supported
2598 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2599 static int sdl_js_axis[MAX_PLAYERS][2];
2600 static int sdl_js_button[MAX_PLAYERS][2];
2601 static boolean sdl_is_controller[MAX_PLAYERS];
2603 void SDLClearJoystickState()
2607 for (i = 0; i < MAX_PLAYERS; i++)
2609 for (j = 0; j < 2; j++)
2611 sdl_js_axis_raw[i][j] = -1;
2612 sdl_js_axis[i][j] = 0;
2613 sdl_js_button[i][j] = 0;
2618 boolean SDLOpenJoystick(int nr)
2620 if (nr < 0 || nr >= MAX_PLAYERS)
2623 #if defined(TARGET_SDL2)
2624 sdl_is_controller[nr] = SDL_IsGameController(nr);
2626 sdl_is_controller[nr] = FALSE;
2630 Error(ERR_DEBUG, "opening joystick %d (%s)",
2631 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2634 #if defined(TARGET_SDL2)
2635 if (sdl_is_controller[nr])
2636 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2638 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2640 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2643 return (sdl_joystick[nr] != NULL);
2646 void SDLCloseJoystick(int nr)
2648 if (nr < 0 || nr >= MAX_PLAYERS)
2652 Error(ERR_DEBUG, "closing joystick %d", nr);
2655 #if defined(TARGET_SDL2)
2656 if (sdl_is_controller[nr])
2657 SDL_GameControllerClose(sdl_joystick[nr]);
2659 SDL_JoystickClose(sdl_joystick[nr]);
2661 SDL_JoystickClose(sdl_joystick[nr]);
2664 sdl_joystick[nr] = NULL;
2667 boolean SDLCheckJoystickOpened(int nr)
2669 if (nr < 0 || nr >= MAX_PLAYERS)
2672 #if defined(TARGET_SDL2)
2673 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2675 return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
2679 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2681 #if defined(TARGET_SDL2)
2682 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2683 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2684 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2685 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2687 int axis_id = axis_id_raw % 2;
2690 if (nr < 0 || nr >= MAX_PLAYERS)
2696 // prevent (slightly jittering, but centered) axis A from resetting axis B
2697 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2698 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2701 sdl_js_axis[nr][axis_id] = axis_value;
2702 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2705 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2707 #if defined(TARGET_SDL2)
2708 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2709 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2710 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2711 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2712 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2713 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2714 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2715 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2718 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2719 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2720 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2721 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2722 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2723 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2724 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2725 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2727 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2728 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2729 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2730 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2731 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2733 int button_id = button_id_raw % 2;
2736 if (nr < 0 || nr >= MAX_PLAYERS)
2739 if (button_id == -1)
2742 sdl_js_button[nr][button_id] = button_state;
2745 void HandleJoystickEvent(Event *event)
2749 #if defined(TARGET_SDL2)
2750 case SDL_CONTROLLERDEVICEADDED:
2752 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2753 event->cdevice.which);
2758 case SDL_CONTROLLERDEVICEREMOVED:
2760 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2761 event->cdevice.which);
2766 case SDL_CONTROLLERAXISMOTION:
2768 Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2769 event->caxis.which, event->caxis.axis, event->caxis.value);
2771 setJoystickAxis(event->caxis.which,
2773 event->caxis.value);
2776 case SDL_CONTROLLERBUTTONDOWN:
2778 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2779 event->cbutton.which, event->cbutton.button);
2781 setJoystickButton(event->cbutton.which,
2782 event->cbutton.button,
2786 case SDL_CONTROLLERBUTTONUP:
2788 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2789 event->cbutton.which, event->cbutton.button);
2791 setJoystickButton(event->cbutton.which,
2792 event->cbutton.button,
2797 case SDL_JOYAXISMOTION:
2798 if (sdl_is_controller[event->jaxis.which])
2802 Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2803 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2805 if (event->jaxis.axis < 4)
2806 setJoystickAxis(event->jaxis.which,
2808 event->jaxis.value);
2811 case SDL_JOYBUTTONDOWN:
2812 if (sdl_is_controller[event->jaxis.which])
2816 Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2817 event->jbutton.which, event->jbutton.button);
2819 if (event->jbutton.button < 4)
2820 setJoystickButton(event->jbutton.which,
2821 event->jbutton.button,
2825 case SDL_JOYBUTTONUP:
2826 if (sdl_is_controller[event->jaxis.which])
2830 Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2831 event->jbutton.which, event->jbutton.button);
2833 if (event->jbutton.button < 4)
2834 setJoystickButton(event->jbutton.which,
2835 event->jbutton.button,
2844 void SDLInitJoysticks()
2846 static boolean sdl_joystick_subsystem_initialized = FALSE;
2847 boolean print_warning = !sdl_joystick_subsystem_initialized;
2848 #if defined(TARGET_SDL2)
2849 char *mappings_file_base = getPath2(options.conf_directory,
2850 GAMECONTROLLER_BASENAME);
2851 char *mappings_file_user = getPath2(getUserGameDataDir(),
2852 GAMECONTROLLER_BASENAME);
2857 if (!sdl_joystick_subsystem_initialized)
2859 sdl_joystick_subsystem_initialized = TRUE;
2861 #if defined(TARGET_SDL2)
2862 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2864 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2866 if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
2869 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2873 #if defined(TARGET_SDL2)
2874 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2876 if (num_mappings != -1)
2877 Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2879 Error(ERR_WARN, "no game controller base mappings found");
2881 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2883 if (num_mappings != -1)
2884 Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2886 Error(ERR_WARN, "no game controller user mappings found");
2888 Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2890 checked_free(mappings_file_base);
2891 checked_free(mappings_file_user);
2893 for (i = 0; i < SDL_NumJoysticks(); i++)
2895 const char *name, *type;
2897 if (SDL_IsGameController(i))
2899 name = SDL_GameControllerNameForIndex(i);
2900 type = "game controller";
2904 name = SDL_JoystickNameForIndex(i);
2908 Error(ERR_INFO, "- joystick %d (%s): '%s'",
2909 i, type, (name ? name : "(Unknown)"));
2914 /* assign joysticks from configured to connected joystick for all players */
2915 for (i = 0; i < MAX_PLAYERS; i++)
2917 /* get configured joystick for this player */
2918 char *device_name = setup.input[i].joy.device_name;
2919 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2921 if (joystick_nr >= SDL_NumJoysticks())
2923 if (setup.input[i].use_joystick && print_warning)
2924 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2929 /* store configured joystick number for each player */
2930 joystick.nr[i] = joystick_nr;
2933 /* now open all connected joysticks (regardless if configured or not) */
2934 for (i = 0; i < SDL_NumJoysticks(); i++)
2936 /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
2937 if (SDLCheckJoystickOpened(i))
2938 SDLCloseJoystick(i);
2940 if (SDLOpenJoystick(i))
2941 joystick.status = JOYSTICK_ACTIVATED;
2942 else if (print_warning)
2943 Error(ERR_WARN, "cannot open joystick %d", i);
2946 SDLClearJoystickState();
2949 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2951 if (nr < 0 || nr >= MAX_PLAYERS)
2955 *x = sdl_js_axis[nr][0];
2957 *y = sdl_js_axis[nr][1];
2960 *b1 = sdl_js_button[nr][0];
2962 *b2 = sdl_js_button[nr][1];
2968 /* ========================================================================= */
2969 /* touch input overlay functions */
2970 /* ========================================================================= */
2972 #if defined(USE_TOUCH_INPUT_OVERLAY)
2973 static void DrawTouchInputOverlay()
2975 static SDL_Texture *texture = NULL;
2976 static boolean initialized = FALSE;
2977 static boolean deactivated = TRUE;
2978 static int width = 0, height = 0;
2979 static int alpha_max = SDL_ALPHA_OPAQUE / 2;
2980 static int alpha_step = 5;
2981 static int alpha_last = 0;
2982 static int alpha = 0;
2983 boolean active = (overlay.enabled && overlay.active);
2985 if (!active && deactivated)
2990 if (alpha < alpha_max)
2991 alpha = MIN(alpha + alpha_step, alpha_max);
2993 deactivated = FALSE;
2997 alpha = MAX(0, alpha - alpha_step);
3005 char *basename = "overlay/VirtualButtons.png";
3006 char *filename = getCustomImageFilename(basename);
3008 if (filename == NULL)
3009 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
3011 SDL_Surface *surface;
3013 if ((surface = IMG_Load(filename)) == NULL)
3014 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
3017 height = surface->h;
3019 /* set black pixel to transparent if no alpha channel / transparent color */
3020 if (!SDLHasAlpha(surface) &&
3021 !SDLHasColorKey(surface))
3022 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
3023 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
3025 if ((texture = SDLCreateTextureFromSurface(surface)) == NULL)
3026 Error(ERR_EXIT, "SDLCreateTextureFromSurface() failed");
3028 SDL_FreeSurface(surface);
3030 SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
3031 SDL_SetTextureAlphaMod(texture, alpha_max);
3036 if (alpha != alpha_last)
3037 SDL_SetTextureAlphaMod(texture, alpha);
3041 float ratio_overlay = (float) width / height;
3042 float ratio_screen = (float) video.screen_width / video.screen_height;
3043 int width_scaled, height_scaled;
3046 if (ratio_overlay > ratio_screen)
3048 width_scaled = video.screen_width;
3049 height_scaled = video.screen_height * ratio_screen / ratio_overlay;
3051 ypos = video.screen_height - height_scaled;
3055 width_scaled = video.screen_width * ratio_overlay / ratio_screen;
3056 height_scaled = video.screen_height;
3057 xpos = (video.screen_width - width_scaled) / 2;
3061 SDL_Rect src_rect = { 0, 0, width, height };
3062 SDL_Rect dst_rect = { xpos, ypos, width_scaled, height_scaled };
3064 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);