1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
18 #define ENABLE_UNUSED_CODE 0 /* currently unused functions */
20 #define DEBUG_JOYSTICKS 0
23 /* ========================================================================= */
25 /* ========================================================================= */
27 /* SDL internal variables */
28 #if defined(TARGET_SDL2)
29 static SDL_Window *sdl_window = NULL;
30 static SDL_Renderer *sdl_renderer = NULL;
31 static SDL_Texture *sdl_texture_stream = NULL;
32 static SDL_Texture *sdl_texture_target = NULL;
33 static boolean fullscreen_enabled = FALSE;
36 static boolean limit_screen_updates = FALSE;
39 /* functions from SGE library */
40 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
42 #if defined(USE_TOUCH_INPUT_OVERLAY)
43 /* functions to draw overlay graphics for touch device input */
44 static void DrawTouchInputOverlay();
47 void SDLLimitScreenUpdates(boolean enable)
49 limit_screen_updates = enable;
52 static void FinalizeScreen(int draw_target)
54 // copy global animations to render target buffer, if defined (below border)
55 if (gfx.draw_global_anim_function != NULL)
56 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
58 // copy global masked border to render target buffer, if defined
59 if (gfx.draw_global_border_function != NULL)
60 gfx.draw_global_border_function(draw_target);
62 // copy global animations to render target buffer, if defined (above border)
63 if (gfx.draw_global_anim_function != NULL)
64 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
67 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
69 static unsigned int update_screen_delay = 0;
70 unsigned int update_screen_delay_value = 50; /* (milliseconds) */
71 SDL_Surface *screen = backbuffer->surface;
73 if (limit_screen_updates &&
74 !DelayReached(&update_screen_delay, update_screen_delay_value))
77 LimitScreenUpdates(FALSE);
81 static int LastFrameCounter = 0;
82 boolean changed = (FrameCounter != LastFrameCounter);
84 printf("::: FrameCounter == %d [%s]\n", FrameCounter,
85 (changed ? "-" : "SAME FRAME UPDATED"));
87 LastFrameCounter = FrameCounter;
96 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
97 gfx.final_screen_bitmap != NULL) // may not be initialized yet
99 // draw global animations using bitmaps instead of using textures
100 // to prevent texture scaling artefacts (this is potentially slower)
102 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
103 gfx.win_xsize, gfx.win_ysize, 0, 0);
105 FinalizeScreen(DRAW_TO_SCREEN);
107 screen = gfx.final_screen_bitmap->surface;
109 // force full window redraw
113 #if defined(TARGET_SDL2)
114 SDL_Texture *sdl_texture = sdl_texture_stream;
116 // deactivate use of target texture if render targets are not supported
117 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
118 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
119 sdl_texture_target == NULL)
120 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
122 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
123 sdl_texture = sdl_texture_target;
127 int bytes_x = screen->pitch / video.width;
128 int bytes_y = screen->pitch;
130 SDL_UpdateTexture(sdl_texture, rect,
131 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
136 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
139 int xoff = video.screen_xoffset;
140 int yoff = video.screen_yoffset;
141 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
142 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
143 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
145 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
146 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
147 dst_rect2 = &dst_rect_screen;
149 dst_rect1 = &dst_rect_screen;
151 #if defined(HAS_SCREEN_KEYBOARD)
152 if (video.shifted_up || video.shifted_up_delay)
154 int time_current = SDL_GetTicks();
155 int pos = video.shifted_up_pos;
156 int pos_last = video.shifted_up_pos_last;
158 if (!DelayReachedExt(&video.shifted_up_delay, video.shifted_up_delay_value,
161 int delay = time_current - video.shifted_up_delay;
162 int delay_value = video.shifted_up_delay_value;
164 pos = pos_last + (pos - pos_last) * delay / delay_value;
168 video.shifted_up_pos_last = pos;
169 video.shifted_up_delay = 0;
172 SDL_Rect src_rect_up = { 0, pos, video.width, video.height - pos };
173 SDL_Rect dst_rect_up = { xoff, yoff, video.width, video.height - pos };
175 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
176 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
178 src_rect2 = &src_rect_up;
179 dst_rect2 = &dst_rect_up;
183 src_rect1 = &src_rect_up;
184 dst_rect1 = &dst_rect_up;
189 // clear render target buffer
190 SDL_RenderClear(sdl_renderer);
192 // set renderer to use target texture for rendering
193 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
194 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
195 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
197 // copy backbuffer texture to render target buffer
198 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
199 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
201 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
202 FinalizeScreen(DRAW_TO_SCREEN);
204 // when using target texture, copy it to screen buffer
205 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
206 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
208 SDL_SetRenderTarget(sdl_renderer, NULL);
209 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
212 #if defined(USE_TOUCH_INPUT_OVERLAY)
213 // draw overlay graphics for touch device input, if needed
214 DrawTouchInputOverlay();
219 // global synchronization point of the game to align video frame delay
220 if (with_frame_delay)
221 WaitUntilDelayReached(&video.frame_delay, video.frame_delay_value);
223 #if defined(TARGET_SDL2)
224 // show render target buffer on screen
225 SDL_RenderPresent(sdl_renderer);
228 SDL_UpdateRects(screen, 1, rect);
230 SDL_UpdateRect(screen, 0, 0, 0, 0);
234 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
236 UpdateScreenExt(rect, TRUE);
239 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
241 UpdateScreenExt(rect, FALSE);
244 static void SDLSetWindowIcon(char *basename)
246 /* (setting the window icon on Mac OS X would replace the high-quality
247 dock icon with the currently smaller (and uglier) icon from file) */
249 #if !defined(PLATFORM_MACOSX)
250 char *filename = getCustomImageFilename(basename);
251 SDL_Surface *surface;
253 if (filename == NULL)
255 Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
260 if ((surface = IMG_Load(filename)) == NULL)
262 Error(ERR_WARN, "IMG_Load() failed: %s", SDL_GetError());
267 /* set transparent color */
268 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
269 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
271 #if defined(TARGET_SDL2)
272 SDL_SetWindowIcon(sdl_window, surface);
274 SDL_WM_SetIcon(surface, NULL);
279 #if defined(TARGET_SDL2)
281 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
282 SDL_PixelFormat *format2)
284 return (format1->format == format2->format &&
285 format1->BitsPerPixel == format2->BitsPerPixel &&
286 format1->BytesPerPixel == format2->BytesPerPixel &&
287 format1->Rmask == format2->Rmask &&
288 format1->Gmask == format2->Gmask &&
289 format1->Bmask == format2->Bmask);
292 static Pixel SDLGetColorKey(SDL_Surface *surface)
296 if (SDL_GetColorKey(surface, &color_key) != 0)
302 static boolean SDLHasColorKey(SDL_Surface *surface)
304 return (SDLGetColorKey(surface) != -1);
307 static boolean SDLHasAlpha(SDL_Surface *surface)
309 SDL_BlendMode blend_mode;
311 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
314 return (blend_mode == SDL_BLENDMODE_BLEND);
317 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
319 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
321 SDL_SetSurfaceBlendMode(surface, blend_mode);
322 SDL_SetSurfaceAlphaMod(surface, alpha);
325 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
327 SDL_PixelFormat format;
328 SDL_Surface *new_surface;
333 if (backbuffer && backbuffer->surface)
335 format = *backbuffer->surface->format;
336 format.Amask = surface->format->Amask; // keep alpha channel
340 format = *surface->format;
343 new_surface = SDL_ConvertSurface(surface, &format, 0);
345 if (new_surface == NULL)
346 Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
351 boolean SDLSetNativeSurface(SDL_Surface **surface)
353 SDL_Surface *new_surface;
355 if (surface == NULL ||
357 backbuffer == NULL ||
358 backbuffer->surface == NULL)
361 // if pixel format already optimized for destination surface, do nothing
362 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
365 new_surface = SDLGetNativeSurface(*surface);
367 SDL_FreeSurface(*surface);
369 *surface = new_surface;
376 static Pixel SDLGetColorKey(SDL_Surface *surface)
378 if ((surface->flags & SDL_SRCCOLORKEY) == 0)
381 return surface->format->colorkey;
384 static boolean SDLHasColorKey(SDL_Surface *surface)
386 return (SDLGetColorKey(surface) != -1);
389 static boolean SDLHasAlpha(SDL_Surface *surface)
391 return ((surface->flags & SDL_SRCALPHA) != 0);
394 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
396 SDL_SetAlpha(surface, (set ? SDL_SRCALPHA : 0), alpha);
399 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
401 SDL_Surface *new_surface;
406 if (!video.initialized)
407 new_surface = SDL_ConvertSurface(surface, surface->format, SURFACE_FLAGS);
408 else if (SDLHasAlpha(surface))
409 new_surface = SDL_DisplayFormatAlpha(surface);
411 new_surface = SDL_DisplayFormat(surface);
413 if (new_surface == NULL)
414 Error(ERR_EXIT, "%s() failed: %s",
415 (video.initialized ? "SDL_DisplayFormat" : "SDL_ConvertSurface"),
421 boolean SDLSetNativeSurface(SDL_Surface **surface)
423 SDL_Surface *new_surface;
425 if (surface == NULL ||
430 new_surface = SDLGetNativeSurface(*surface);
432 SDL_FreeSurface(*surface);
434 *surface = new_surface;
441 #if defined(TARGET_SDL2)
442 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
444 if (program.headless)
447 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
450 Error(ERR_EXIT, "SDL_CreateTextureFromSurface() failed: %s",
457 void SDLCreateBitmapTextures(Bitmap *bitmap)
459 #if defined(TARGET_SDL2)
464 SDL_DestroyTexture(bitmap->texture);
465 if (bitmap->texture_masked)
466 SDL_DestroyTexture(bitmap->texture_masked);
468 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
469 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
473 void SDLFreeBitmapTextures(Bitmap *bitmap)
475 #if defined(TARGET_SDL2)
480 SDL_DestroyTexture(bitmap->texture);
481 if (bitmap->texture_masked)
482 SDL_DestroyTexture(bitmap->texture_masked);
484 bitmap->texture = NULL;
485 bitmap->texture_masked = NULL;
489 void SDLInitVideoDisplay(void)
491 #if !defined(TARGET_SDL2)
492 if (!strEqual(setup.system.sdl_videodriver, ARG_DEFAULT))
493 SDL_putenv(getStringCat2("SDL_VIDEODRIVER=", setup.system.sdl_videodriver));
495 SDL_putenv("SDL_VIDEO_CENTERED=1");
498 /* initialize SDL video */
499 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
500 Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
502 /* set default SDL depth */
503 #if !defined(TARGET_SDL2)
504 video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
506 video.default_depth = 32; // (how to determine video depth in SDL2?)
510 inline static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
512 if (program.headless)
515 video.window_scaling_percent = setup.window_scaling_percent;
516 video.window_scaling_quality = setup.window_scaling_quality;
518 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
520 #if defined(TARGET_SDL2)
521 // SDL 2.0: support for (desktop) fullscreen mode available
522 video.fullscreen_available = TRUE;
524 // SDL 1.2: no support for fullscreen mode in R'n'D anymore
525 video.fullscreen_available = FALSE;
528 /* open SDL video output device (window or fullscreen mode) */
529 if (!SDLSetVideoMode(fullscreen))
530 Error(ERR_EXIT, "setting video mode failed");
532 /* !!! SDL2 can only set the window icon if the window already exists !!! */
533 /* set window icon */
534 SDLSetWindowIcon(program.icon_filename);
536 /* set window and icon title */
540 inline static void SDLInitVideoBuffer_DrawBuffer()
542 /* SDL cannot directly draw to the visible video framebuffer like X11,
543 but always uses a backbuffer, which is then blitted to the visible
544 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
545 visible video framebuffer with 'SDL_Flip', if the hardware supports
546 this). Therefore do not use an additional backbuffer for drawing, but
547 use a symbolic buffer (distinguishable from the SDL backbuffer) called
548 'window', which indicates that the SDL backbuffer should be updated to
549 the visible video framebuffer when attempting to blit to it.
551 For convenience, it seems to be a good idea to create this symbolic
552 buffer 'window' at the same size as the SDL backbuffer. Although it
553 should never be drawn to directly, it would do no harm nevertheless. */
555 /* create additional (symbolic) buffer for double-buffering */
556 ReCreateBitmap(&window, video.width, video.height);
558 /* create dummy drawing buffer for headless mode, if needed */
559 if (program.headless)
560 ReCreateBitmap(&backbuffer, video.width, video.height);
563 void SDLInitVideoBuffer(boolean fullscreen)
565 SDLInitVideoBuffer_VideoBuffer(fullscreen);
566 SDLInitVideoBuffer_DrawBuffer();
569 static boolean SDLCreateScreen(boolean fullscreen)
571 SDL_Surface *new_surface = NULL;
573 #if defined(TARGET_SDL2)
574 int surface_flags_window = SURFACE_FLAGS | SDL_WINDOW_RESIZABLE;
575 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
577 int surface_flags_window = SURFACE_FLAGS;
578 int surface_flags_fullscreen = SURFACE_FLAGS; // (no fullscreen in SDL 1.2)
581 #if defined(TARGET_SDL2)
583 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
585 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
586 _without_ enabling 2D/3D acceleration and/or guest additions installed,
587 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
588 it will try to use accelerated graphics and apparently fails miserably) */
589 int renderer_flags = SDL_RENDERER_SOFTWARE;
592 SDLSetScreenSizeAndOffsets(video.width, video.height);
595 int width = video.width;
596 int height = video.height;
597 int screen_width = video.screen_width;
598 int screen_height = video.screen_height;
599 int surface_flags = (fullscreen ? surface_flags_fullscreen :
600 surface_flags_window);
602 // default window size is unscaled
603 video.window_width = screen_width;
604 video.window_height = screen_height;
606 #if defined(TARGET_SDL2)
608 // store if initial screen mode is fullscreen mode when changing screen size
609 video.fullscreen_initial = fullscreen;
611 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
613 video.window_width = window_scaling_factor * screen_width;
614 video.window_height = window_scaling_factor * screen_height;
616 if (sdl_texture_stream)
618 SDL_DestroyTexture(sdl_texture_stream);
619 sdl_texture_stream = NULL;
622 if (sdl_texture_target)
624 SDL_DestroyTexture(sdl_texture_target);
625 sdl_texture_target = NULL;
628 if (!(fullscreen && fullscreen_enabled))
632 SDL_DestroyRenderer(sdl_renderer);
638 SDL_DestroyWindow(sdl_window);
643 if (sdl_window == NULL)
644 sdl_window = SDL_CreateWindow(program.window_title,
645 SDL_WINDOWPOS_CENTERED,
646 SDL_WINDOWPOS_CENTERED,
651 if (sdl_window != NULL)
653 if (sdl_renderer == NULL)
654 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
656 if (sdl_renderer != NULL)
658 SDL_RenderSetLogicalSize(sdl_renderer, screen_width, screen_height);
659 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
660 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
662 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
663 SDL_PIXELFORMAT_ARGB8888,
664 SDL_TEXTUREACCESS_STREAMING,
667 if (SDL_RenderTargetSupported(sdl_renderer))
668 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
669 SDL_PIXELFORMAT_ARGB8888,
670 SDL_TEXTUREACCESS_TARGET,
673 if (sdl_texture_stream != NULL)
675 // use SDL default values for RGB masks and no alpha channel
676 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
678 if (new_surface == NULL)
679 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
683 Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
688 Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
693 Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
698 if (gfx.final_screen_bitmap == NULL)
699 gfx.final_screen_bitmap = CreateBitmapStruct();
701 gfx.final_screen_bitmap->width = width;
702 gfx.final_screen_bitmap->height = height;
704 gfx.final_screen_bitmap->surface =
705 SDL_SetVideoMode(width, height, video.depth, surface_flags);
707 if (gfx.final_screen_bitmap->surface != NULL)
710 SDL_CreateRGBSurface(surface_flags, width, height, video.depth, 0,0,0, 0);
712 if (new_surface == NULL)
713 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
716 new_surface = gfx.final_screen_bitmap->surface;
717 gfx.final_screen_bitmap = NULL;
723 Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
727 #if defined(TARGET_SDL2)
728 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
729 if (new_surface != NULL)
730 fullscreen_enabled = fullscreen;
733 if (backbuffer == NULL)
734 backbuffer = CreateBitmapStruct();
736 backbuffer->width = video.width;
737 backbuffer->height = video.height;
739 if (backbuffer->surface)
740 SDL_FreeSurface(backbuffer->surface);
742 backbuffer->surface = new_surface;
744 return (new_surface != NULL);
747 boolean SDLSetVideoMode(boolean fullscreen)
749 boolean success = FALSE;
753 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
755 /* switch display to fullscreen mode, if available */
756 success = SDLCreateScreen(TRUE);
760 /* switching display to fullscreen mode failed -- do not try it again */
761 video.fullscreen_available = FALSE;
765 video.fullscreen_enabled = TRUE;
769 if ((!fullscreen && video.fullscreen_enabled) || !success)
771 /* switch display to window mode */
772 success = SDLCreateScreen(FALSE);
776 /* switching display to window mode failed -- should not happen */
780 video.fullscreen_enabled = FALSE;
781 video.window_scaling_percent = setup.window_scaling_percent;
782 video.window_scaling_quality = setup.window_scaling_quality;
784 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
788 #if defined(TARGET_SDL2)
789 SDLRedrawWindow(); // map window
793 #if defined(PLATFORM_WIN32)
794 // experimental drag and drop code
796 SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
799 SDL_SysWMinfo wminfo;
801 boolean wminfo_success = FALSE;
803 SDL_VERSION(&wminfo.version);
804 #if defined(TARGET_SDL2)
806 wminfo_success = SDL_GetWindowWMInfo(sdl_window, &wminfo);
808 wminfo_success = (SDL_GetWMInfo(&wminfo) == 1);
813 #if defined(TARGET_SDL2)
814 hwnd = wminfo.info.win.window;
816 hwnd = wminfo.window;
819 DragAcceptFiles(hwnd, TRUE);
828 void SDLSetWindowTitle()
830 #if defined(TARGET_SDL2)
831 if (sdl_window == NULL)
834 SDL_SetWindowTitle(sdl_window, program.window_title);
836 SDL_WM_SetCaption(program.window_title, program.window_title);
840 #if defined(TARGET_SDL2)
841 void SDLSetWindowScaling(int window_scaling_percent)
843 if (sdl_window == NULL)
846 float window_scaling_factor = (float)window_scaling_percent / 100;
847 int new_window_width = (int)(window_scaling_factor * video.screen_width);
848 int new_window_height = (int)(window_scaling_factor * video.screen_height);
850 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
852 video.window_scaling_percent = window_scaling_percent;
853 video.window_width = new_window_width;
854 video.window_height = new_window_height;
859 void SDLSetWindowScalingQuality(char *window_scaling_quality)
861 SDL_Texture *new_texture;
863 if (sdl_texture_stream == NULL)
866 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
868 new_texture = SDL_CreateTexture(sdl_renderer,
869 SDL_PIXELFORMAT_ARGB8888,
870 SDL_TEXTUREACCESS_STREAMING,
871 video.width, video.height);
873 if (new_texture != NULL)
875 SDL_DestroyTexture(sdl_texture_stream);
877 sdl_texture_stream = new_texture;
880 if (SDL_RenderTargetSupported(sdl_renderer))
881 new_texture = SDL_CreateTexture(sdl_renderer,
882 SDL_PIXELFORMAT_ARGB8888,
883 SDL_TEXTUREACCESS_TARGET,
884 video.width, video.height);
888 if (new_texture != NULL)
890 SDL_DestroyTexture(sdl_texture_target);
892 sdl_texture_target = new_texture;
897 video.window_scaling_quality = window_scaling_quality;
900 void SDLSetWindowFullscreen(boolean fullscreen)
902 if (sdl_window == NULL)
905 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
907 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
908 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
910 // if screen size was changed in fullscreen mode, correct desktop window size
911 if (!fullscreen && video.fullscreen_initial)
913 SDLSetWindowScaling(setup.window_scaling_percent);
914 SDL_SetWindowPosition(sdl_window,
915 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
917 video.fullscreen_initial = FALSE;
921 void SDLSetDisplaySize()
923 SDL_Rect display_bounds;
925 SDL_GetDisplayBounds(0, &display_bounds);
927 video.display_width = display_bounds.w;
928 video.display_height = display_bounds.h;
931 Error(ERR_DEBUG, "SDL real screen size: %d x %d",
932 video.display_width, video.display_height);
936 void SDLSetScreenSizeAndOffsets(int width, int height)
938 // set default video screen size and offsets
939 video.screen_width = width;
940 video.screen_height = height;
941 video.screen_xoffset = 0;
942 video.screen_yoffset = 0;
944 #if defined(USE_COMPLETE_DISPLAY)
945 float ratio_video = (float) width / height;
946 float ratio_display = (float) video.display_width / video.display_height;
948 if (ratio_video != ratio_display)
950 // adjust drawable screen size to cover the whole device display
952 if (ratio_video < ratio_display)
953 video.screen_width *= ratio_display / ratio_video;
955 video.screen_height *= ratio_video / ratio_display;
957 video.screen_xoffset = (video.screen_width - width) / 2;
958 video.screen_yoffset = (video.screen_height - height) / 2;
961 Error(ERR_DEBUG, "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
963 video.screen_width, video.screen_height,
964 ratio_video, ratio_display);
970 void SDLSetScreenSizeForRenderer(int width, int height)
972 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
975 void SDLSetScreenProperties()
977 SDLSetScreenSizeAndOffsets(video.width, video.height);
978 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
983 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
985 #if defined(TARGET_SDL2)
986 video.screen_rendering_mode =
987 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
988 SPECIAL_RENDERING_BITMAP :
989 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
990 SPECIAL_RENDERING_TARGET:
991 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
992 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
994 video.screen_rendering_mode = SPECIAL_RENDERING_BITMAP;
998 void SDLRedrawWindow()
1000 UpdateScreen_WithoutFrameDelay(NULL);
1003 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
1006 if (program.headless)
1009 SDL_Surface *surface =
1010 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
1012 if (surface == NULL)
1013 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
1015 SDLSetNativeSurface(&surface);
1017 bitmap->surface = surface;
1020 void SDLFreeBitmapPointers(Bitmap *bitmap)
1022 if (bitmap->surface)
1023 SDL_FreeSurface(bitmap->surface);
1024 if (bitmap->surface_masked)
1025 SDL_FreeSurface(bitmap->surface_masked);
1027 bitmap->surface = NULL;
1028 bitmap->surface_masked = NULL;
1030 #if defined(TARGET_SDL2)
1031 if (bitmap->texture)
1032 SDL_DestroyTexture(bitmap->texture);
1033 if (bitmap->texture_masked)
1034 SDL_DestroyTexture(bitmap->texture_masked);
1036 bitmap->texture = NULL;
1037 bitmap->texture_masked = NULL;
1041 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1042 int src_x, int src_y, int width, int height,
1043 int dst_x, int dst_y, int mask_mode)
1045 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1046 SDL_Rect src_rect, dst_rect;
1051 src_rect.h = height;
1056 dst_rect.h = height;
1058 // if (src_bitmap != backbuffer || dst_bitmap != window)
1059 if (!(src_bitmap == backbuffer && dst_bitmap == window))
1060 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
1061 src_bitmap->surface_masked : src_bitmap->surface),
1062 &src_rect, real_dst_bitmap->surface, &dst_rect);
1064 if (dst_bitmap == window)
1065 UpdateScreen_WithFrameDelay(&dst_rect);
1068 void SDLBlitTexture(Bitmap *bitmap,
1069 int src_x, int src_y, int width, int height,
1070 int dst_x, int dst_y, int mask_mode)
1072 #if defined(TARGET_SDL2)
1073 SDL_Texture *texture;
1078 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1080 if (texture == NULL)
1086 src_rect.h = height;
1091 dst_rect.h = height;
1093 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1097 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1100 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1108 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1110 if (dst_bitmap == window)
1111 UpdateScreen_WithFrameDelay(&rect);
1114 void PrepareFadeBitmap(int draw_target)
1116 Bitmap *fade_bitmap =
1117 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1118 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1120 if (fade_bitmap == NULL)
1123 // copy backbuffer to fading buffer
1124 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1126 // add border and animations to fading buffer
1127 FinalizeScreen(draw_target);
1130 void SDLFadeRectangle(int x, int y, int width, int height,
1131 int fade_mode, int fade_delay, int post_delay,
1132 void (*draw_border_function)(void))
1134 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1135 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1136 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1137 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1138 SDL_Surface *surface_screen = backbuffer->surface;
1139 SDL_Rect src_rect, dst_rect;
1141 int src_x = x, src_y = y;
1142 int dst_x = x, dst_y = y;
1143 unsigned int time_last, time_current;
1145 // store function for drawing global masked border
1146 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1148 // deactivate drawing of global border while fading, if needed
1149 if (draw_border_function == NULL)
1150 gfx.draw_global_border_function = NULL;
1155 src_rect.h = height;
1159 dst_rect.w = width; /* (ignored) */
1160 dst_rect.h = height; /* (ignored) */
1162 dst_rect2 = dst_rect;
1164 // before fading in, store backbuffer (without animation graphics)
1165 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1166 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1168 /* copy source and target surfaces to temporary surfaces for fading */
1169 if (fade_mode & FADE_TYPE_TRANSFORM)
1171 // (source and target fading buffer already prepared)
1173 else if (fade_mode & FADE_TYPE_FADE_IN)
1175 // (target fading buffer already prepared)
1176 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1178 else /* FADE_TYPE_FADE_OUT */
1180 // (source fading buffer already prepared)
1181 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1184 time_current = SDL_GetTicks();
1186 if (fade_mode == FADE_MODE_MELT)
1188 boolean done = FALSE;
1189 int melt_pixels = 2;
1190 int melt_columns = width / melt_pixels;
1191 int ypos[melt_columns];
1192 int max_steps = height / 8 + 32;
1197 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1199 SDLSetAlpha(surface_target, FALSE, 0); /* disable alpha blending */
1201 ypos[0] = -GetSimpleRandom(16);
1203 for (i = 1 ; i < melt_columns; i++)
1205 int r = GetSimpleRandom(3) - 1; /* randomly choose from { -1, 0, -1 } */
1207 ypos[i] = ypos[i - 1] + r;
1220 time_last = time_current;
1221 time_current = SDL_GetTicks();
1222 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1223 steps_final = MIN(MAX(0, steps), max_steps);
1227 done = (steps_done >= steps_final);
1229 for (i = 0 ; i < melt_columns; i++)
1237 else if (ypos[i] < height)
1242 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1244 if (ypos[i] + dy >= height)
1245 dy = height - ypos[i];
1247 /* copy part of (appearing) target surface to upper area */
1248 src_rect.x = src_x + i * melt_pixels;
1249 // src_rect.y = src_y + ypos[i];
1251 src_rect.w = melt_pixels;
1253 src_rect.h = ypos[i] + dy;
1255 dst_rect.x = dst_x + i * melt_pixels;
1256 // dst_rect.y = dst_y + ypos[i];
1259 if (steps_done >= steps_final)
1260 SDL_BlitSurface(surface_target, &src_rect,
1261 surface_screen, &dst_rect);
1265 /* copy part of (disappearing) source surface to lower area */
1266 src_rect.x = src_x + i * melt_pixels;
1268 src_rect.w = melt_pixels;
1269 src_rect.h = height - ypos[i];
1271 dst_rect.x = dst_x + i * melt_pixels;
1272 dst_rect.y = dst_y + ypos[i];
1274 if (steps_done >= steps_final)
1275 SDL_BlitSurface(surface_source, &src_rect,
1276 surface_screen, &dst_rect);
1282 src_rect.x = src_x + i * melt_pixels;
1284 src_rect.w = melt_pixels;
1285 src_rect.h = height;
1287 dst_rect.x = dst_x + i * melt_pixels;
1290 if (steps_done >= steps_final)
1291 SDL_BlitSurface(surface_target, &src_rect,
1292 surface_screen, &dst_rect);
1296 if (steps_done >= steps_final)
1298 if (draw_border_function != NULL)
1299 draw_border_function();
1301 UpdateScreen_WithFrameDelay(&dst_rect2);
1305 else if (fade_mode == FADE_MODE_CURTAIN)
1309 int xx_size = width / 2;
1311 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1313 SDLSetAlpha(surface_source, FALSE, 0); /* disable alpha blending */
1315 for (xx = 0; xx < xx_size;)
1317 time_last = time_current;
1318 time_current = SDL_GetTicks();
1319 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1320 xx_final = MIN(MAX(0, xx), xx_size);
1325 src_rect.h = height;
1330 /* draw new (target) image to screen buffer */
1331 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1333 if (xx_final < xx_size)
1335 src_rect.w = xx_size - xx_final;
1336 src_rect.h = height;
1338 /* draw old (source) image to screen buffer (left side) */
1340 src_rect.x = src_x + xx_final;
1343 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1345 /* draw old (source) image to screen buffer (right side) */
1347 src_rect.x = src_x + xx_size;
1348 dst_rect.x = dst_x + xx_size + xx_final;
1350 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1353 if (draw_border_function != NULL)
1354 draw_border_function();
1356 /* only update the region of the screen that is affected from fading */
1357 UpdateScreen_WithFrameDelay(&dst_rect2);
1360 else /* fading in, fading out or cross-fading */
1365 for (alpha = 0.0; alpha < 255.0;)
1367 time_last = time_current;
1368 time_current = SDL_GetTicks();
1369 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1370 alpha_final = MIN(MAX(0, alpha), 255);
1372 /* draw existing (source) image to screen buffer */
1373 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1375 /* draw new (target) image to screen buffer using alpha blending */
1376 SDLSetAlpha(surface_target, TRUE, alpha_final);
1377 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1379 if (draw_border_function != NULL)
1380 draw_border_function();
1382 /* only update the region of the screen that is affected from fading */
1383 UpdateScreen_WithFrameDelay(&dst_rect);
1389 unsigned int time_post_delay;
1391 time_current = SDL_GetTicks();
1392 time_post_delay = time_current + post_delay;
1394 while (time_current < time_post_delay)
1396 // updating the screen contains waiting for frame delay (non-busy)
1397 UpdateScreen_WithFrameDelay(NULL);
1399 time_current = SDL_GetTicks();
1403 // restore function for drawing global masked border
1404 gfx.draw_global_border_function = draw_global_border_function;
1406 // after fading in, restore backbuffer (without animation graphics)
1407 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1408 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1411 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1412 int to_x, int to_y, Uint32 color)
1414 SDL_Surface *surface = dst_bitmap->surface;
1418 swap_numbers(&from_x, &to_x);
1421 swap_numbers(&from_y, &to_y);
1425 rect.w = (to_x - from_x + 1);
1426 rect.h = (to_y - from_y + 1);
1428 SDL_FillRect(surface, &rect, color);
1431 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1432 int to_x, int to_y, Uint32 color)
1434 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1437 #if ENABLE_UNUSED_CODE
1438 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1439 int num_points, Uint32 color)
1444 for (i = 0; i < num_points - 1; i++)
1446 for (x = 0; x < line_width; x++)
1448 for (y = 0; y < line_width; y++)
1450 int dx = x - line_width / 2;
1451 int dy = y - line_width / 2;
1453 if ((x == 0 && y == 0) ||
1454 (x == 0 && y == line_width - 1) ||
1455 (x == line_width - 1 && y == 0) ||
1456 (x == line_width - 1 && y == line_width - 1))
1459 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1460 points[i+1].x + dx, points[i+1].y + dy, color);
1467 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1469 SDL_Surface *surface = src_bitmap->surface;
1471 switch (surface->format->BytesPerPixel)
1473 case 1: /* assuming 8-bpp */
1475 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1479 case 2: /* probably 15-bpp or 16-bpp */
1481 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1485 case 3: /* slow 24-bpp mode; usually not used */
1487 /* does this work? */
1488 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1492 shift = surface->format->Rshift;
1493 color |= *(pix + shift / 8) >> shift;
1494 shift = surface->format->Gshift;
1495 color |= *(pix + shift / 8) >> shift;
1496 shift = surface->format->Bshift;
1497 color |= *(pix + shift / 8) >> shift;
1503 case 4: /* probably 32-bpp */
1505 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1514 /* ========================================================================= */
1515 /* The following functions were taken from the SGE library */
1516 /* (SDL Graphics Extension Library) by Anders Lindström */
1517 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html */
1518 /* ========================================================================= */
1520 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1522 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1524 switch (surface->format->BytesPerPixel)
1528 /* Assuming 8-bpp */
1529 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1535 /* Probably 15-bpp or 16-bpp */
1536 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1542 /* Slow 24-bpp mode, usually not used */
1546 /* Gack - slow, but endian correct */
1547 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1548 shift = surface->format->Rshift;
1549 *(pix+shift/8) = color>>shift;
1550 shift = surface->format->Gshift;
1551 *(pix+shift/8) = color>>shift;
1552 shift = surface->format->Bshift;
1553 *(pix+shift/8) = color>>shift;
1559 /* Probably 32-bpp */
1560 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1567 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1568 Uint8 R, Uint8 G, Uint8 B)
1570 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1573 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1575 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1578 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1580 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1583 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1588 /* Gack - slow, but endian correct */
1589 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1590 shift = surface->format->Rshift;
1591 *(pix+shift/8) = color>>shift;
1592 shift = surface->format->Gshift;
1593 *(pix+shift/8) = color>>shift;
1594 shift = surface->format->Bshift;
1595 *(pix+shift/8) = color>>shift;
1598 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1600 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1603 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1605 switch (dest->format->BytesPerPixel)
1608 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1612 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1616 _PutPixel24(dest,x,y,color);
1620 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1625 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1627 if (SDL_MUSTLOCK(surface))
1629 if (SDL_LockSurface(surface) < 0)
1635 _PutPixel(surface, x, y, color);
1637 if (SDL_MUSTLOCK(surface))
1639 SDL_UnlockSurface(surface);
1643 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1644 Uint8 r, Uint8 g, Uint8 b)
1646 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1649 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1651 if (y >= 0 && y <= dest->h - 1)
1653 switch (dest->format->BytesPerPixel)
1656 return y*dest->pitch;
1660 return y*dest->pitch/2;
1664 return y*dest->pitch;
1668 return y*dest->pitch/4;
1676 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
1678 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1680 switch (surface->format->BytesPerPixel)
1684 /* Assuming 8-bpp */
1685 *((Uint8 *)surface->pixels + ypitch + x) = color;
1691 /* Probably 15-bpp or 16-bpp */
1692 *((Uint16 *)surface->pixels + ypitch + x) = color;
1698 /* Slow 24-bpp mode, usually not used */
1702 /* Gack - slow, but endian correct */
1703 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1704 shift = surface->format->Rshift;
1705 *(pix+shift/8) = color>>shift;
1706 shift = surface->format->Gshift;
1707 *(pix+shift/8) = color>>shift;
1708 shift = surface->format->Bshift;
1709 *(pix+shift/8) = color>>shift;
1715 /* Probably 32-bpp */
1716 *((Uint32 *)surface->pixels + ypitch + x) = color;
1723 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1728 if (SDL_MUSTLOCK(Surface))
1730 if (SDL_LockSurface(Surface) < 0)
1743 /* Do the clipping */
1744 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1748 if (x2 > Surface->w - 1)
1749 x2 = Surface->w - 1;
1756 SDL_FillRect(Surface, &l, Color);
1758 if (SDL_MUSTLOCK(Surface))
1760 SDL_UnlockSurface(Surface);
1764 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1765 Uint8 R, Uint8 G, Uint8 B)
1767 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1770 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
1781 /* Do the clipping */
1782 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1786 if (x2 > Surface->w - 1)
1787 x2 = Surface->w - 1;
1794 SDL_FillRect(Surface, &l, Color);
1797 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1802 if (SDL_MUSTLOCK(Surface))
1804 if (SDL_LockSurface(Surface) < 0)
1817 /* Do the clipping */
1818 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1822 if (y2 > Surface->h - 1)
1823 y2 = Surface->h - 1;
1830 SDL_FillRect(Surface, &l, Color);
1832 if (SDL_MUSTLOCK(Surface))
1834 SDL_UnlockSurface(Surface);
1838 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1839 Uint8 R, Uint8 G, Uint8 B)
1841 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1844 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
1855 /* Do the clipping */
1856 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1860 if (y2 > Surface->h - 1)
1861 y2 = Surface->h - 1;
1868 SDL_FillRect(Surface, &l, Color);
1871 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1872 Sint16 x2, Sint16 y2, Uint32 Color,
1873 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1876 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1881 sdx = (dx < 0) ? -1 : 1;
1882 sdy = (dy < 0) ? -1 : 1;
1894 for (x = 0; x < dx; x++)
1896 Callback(Surface, px, py, Color);
1910 for (y = 0; y < dy; y++)
1912 Callback(Surface, px, py, Color);
1926 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1927 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1928 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1931 sge_DoLine(Surface, X1, Y1, X2, Y2,
1932 SDL_MapRGB(Surface->format, R, G, B), Callback);
1935 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1938 if (SDL_MUSTLOCK(Surface))
1940 if (SDL_LockSurface(Surface) < 0)
1945 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1947 /* unlock the display */
1948 if (SDL_MUSTLOCK(Surface))
1950 SDL_UnlockSurface(Surface);
1954 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1955 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1957 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1960 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1962 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1967 -----------------------------------------------------------------------------
1968 quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1969 -----------------------------------------------------------------------------
1972 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1973 int width, int height, Uint32 color)
1977 for (y = src_y; y < src_y + height; y++)
1979 for (x = src_x; x < src_x + width; x++)
1981 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1983 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1988 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1989 int src_x, int src_y, int width, int height,
1990 int dst_x, int dst_y)
1994 for (y = 0; y < height; y++)
1996 for (x = 0; x < width; x++)
1998 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
2000 if (pixel != BLACK_PIXEL)
2001 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
2007 /* ========================================================================= */
2008 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
2009 /* (Rotozoomer) by Andreas Schiffler */
2010 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html */
2011 /* ========================================================================= */
2014 -----------------------------------------------------------------------------
2017 zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
2018 -----------------------------------------------------------------------------
2029 int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
2032 tColorRGBA *sp, *csp, *dp;
2036 sp = csp = (tColorRGBA *) src->pixels;
2037 dp = (tColorRGBA *) dst->pixels;
2038 dgap = dst->pitch - dst->w * 4;
2040 for (y = 0; y < dst->h; y++)
2044 for (x = 0; x < dst->w; x++)
2046 tColorRGBA *sp0 = sp;
2047 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
2048 tColorRGBA *sp00 = &sp0[0];
2049 tColorRGBA *sp01 = &sp0[1];
2050 tColorRGBA *sp10 = &sp1[0];
2051 tColorRGBA *sp11 = &sp1[1];
2054 /* create new color pixel from all four source color pixels */
2055 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
2056 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
2057 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
2058 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
2063 /* advance source pointers */
2066 /* advance destination pointer */
2070 /* advance source pointer */
2071 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2073 /* advance destination pointers */
2074 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2080 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2082 int x, y, *sax, *say, *csax, *csay;
2084 tColorRGBA *sp, *csp, *csp0, *dp;
2087 /* use specialized zoom function when scaling down to exactly half size */
2088 if (src->w == 2 * dst->w &&
2089 src->h == 2 * dst->h)
2090 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2092 /* variable setup */
2093 sx = (float) src->w / (float) dst->w;
2094 sy = (float) src->h / (float) dst->h;
2096 /* allocate memory for row increments */
2097 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2098 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2100 /* precalculate row increments */
2101 for (x = 0; x <= dst->w; x++)
2102 *csax++ = (int)(sx * x);
2104 for (y = 0; y <= dst->h; y++)
2105 *csay++ = (int)(sy * y);
2108 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2109 dp = (tColorRGBA *) dst->pixels;
2110 dgap = dst->pitch - dst->w * 4;
2113 for (y = 0; y < dst->h; y++)
2118 for (x = 0; x < dst->w; x++)
2123 /* advance source pointers */
2127 /* advance destination pointer */
2131 /* advance source pointer */
2133 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2135 /* advance destination pointers */
2136 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2146 -----------------------------------------------------------------------------
2149 zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2150 -----------------------------------------------------------------------------
2153 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2155 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2156 Uint8 *sp, *dp, *csp;
2159 /* variable setup */
2160 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2161 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2163 /* allocate memory for row increments */
2164 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2165 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2167 /* precalculate row increments */
2170 for (x = 0; x < dst->w; x++)
2173 *csax = (csx >> 16);
2180 for (y = 0; y < dst->h; y++)
2183 *csay = (csy >> 16);
2190 for (x = 0; x < dst->w; x++)
2198 for (y = 0; y < dst->h; y++)
2205 sp = csp = (Uint8 *) src->pixels;
2206 dp = (Uint8 *) dst->pixels;
2207 dgap = dst->pitch - dst->w;
2211 for (y = 0; y < dst->h; y++)
2215 for (x = 0; x < dst->w; x++)
2220 /* advance source pointers */
2224 /* advance destination pointer */
2228 /* advance source pointer (for row) */
2229 csp += ((*csay) * src->pitch);
2232 /* advance destination pointers */
2243 -----------------------------------------------------------------------------
2246 Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2247 'zoomx' and 'zoomy' are scaling factors for width and height.
2248 If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2249 into a 32bit RGBA format on the fly.
2250 -----------------------------------------------------------------------------
2253 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2255 SDL_Surface *zoom_src = NULL;
2256 SDL_Surface *zoom_dst = NULL;
2257 boolean is_converted = FALSE;
2264 /* determine if source surface is 32 bit or 8 bit */
2265 is_32bit = (src->format->BitsPerPixel == 32);
2267 if (is_32bit || src->format->BitsPerPixel == 8)
2269 /* use source surface 'as is' */
2274 /* new source surface is 32 bit with a defined RGB ordering */
2275 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2276 0x000000ff, 0x0000ff00, 0x00ff0000,
2277 (src->format->Amask ? 0xff000000 : 0));
2278 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2280 is_converted = TRUE;
2283 /* allocate surface to completely contain the zoomed surface */
2286 /* target surface is 32 bit with source RGBA/ABGR ordering */
2287 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2288 zoom_src->format->Rmask,
2289 zoom_src->format->Gmask,
2290 zoom_src->format->Bmask,
2291 zoom_src->format->Amask);
2295 /* target surface is 8 bit */
2296 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2300 /* lock source surface */
2301 SDL_LockSurface(zoom_src);
2303 /* check which kind of surface we have */
2306 /* call the 32 bit transformation routine to do the zooming */
2307 zoomSurfaceRGBA(zoom_src, zoom_dst);
2312 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2313 zoom_dst->format->palette->colors[i] =
2314 zoom_src->format->palette->colors[i];
2315 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2317 /* call the 8 bit transformation routine to do the zooming */
2318 zoomSurfaceY(zoom_src, zoom_dst);
2321 /* unlock source surface */
2322 SDL_UnlockSurface(zoom_src);
2324 /* free temporary surface */
2326 SDL_FreeSurface(zoom_src);
2328 /* return destination surface */
2332 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2334 SDL_Surface *new_surface;
2336 if (surface == NULL)
2339 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2340 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2342 /* remove alpha channel from native non-transparent surface, if defined */
2343 SDLSetAlpha(new_surface, FALSE, 0);
2345 /* remove transparent color from native non-transparent surface, if defined */
2346 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2351 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2353 Bitmap *dst_bitmap = CreateBitmapStruct();
2354 SDL_Surface *src_surface = src_bitmap->surface_masked;
2355 SDL_Surface *dst_surface;
2357 dst_width = MAX(1, dst_width); /* prevent zero bitmap width */
2358 dst_height = MAX(1, dst_height); /* prevent zero bitmap height */
2360 dst_bitmap->width = dst_width;
2361 dst_bitmap->height = dst_height;
2363 /* create zoomed temporary surface from source surface */
2364 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2366 /* create native format destination surface from zoomed temporary surface */
2367 SDLSetNativeSurface(&dst_surface);
2369 /* set color key for zoomed surface from source surface, if defined */
2370 if (SDLHasColorKey(src_surface))
2371 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2372 SDLGetColorKey(src_surface));
2374 /* create native non-transparent surface for opaque blitting */
2375 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2377 /* set native transparent surface for masked blitting */
2378 dst_bitmap->surface_masked = dst_surface;
2384 /* ========================================================================= */
2385 /* load image to bitmap */
2386 /* ========================================================================= */
2388 Bitmap *SDLLoadImage(char *filename)
2390 Bitmap *new_bitmap = CreateBitmapStruct();
2391 SDL_Surface *sdl_image_tmp;
2393 if (program.headless)
2395 /* prevent sanity check warnings at later stage */
2396 new_bitmap->width = new_bitmap->height = 1;
2401 print_timestamp_init("SDLLoadImage");
2403 print_timestamp_time(getBaseNamePtr(filename));
2405 /* load image to temporary surface */
2406 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2407 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
2409 print_timestamp_time("IMG_Load");
2411 UPDATE_BUSY_STATE();
2413 /* create native non-transparent surface for current image */
2414 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2415 Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2417 print_timestamp_time("SDLGetNativeSurface (opaque)");
2419 UPDATE_BUSY_STATE();
2421 /* set black pixel to transparent if no alpha channel / transparent color */
2422 if (!SDLHasAlpha(sdl_image_tmp) &&
2423 !SDLHasColorKey(sdl_image_tmp))
2424 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2425 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2427 /* create native transparent surface for current image */
2428 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2429 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2431 print_timestamp_time("SDLGetNativeSurface (masked)");
2433 UPDATE_BUSY_STATE();
2435 /* free temporary surface */
2436 SDL_FreeSurface(sdl_image_tmp);
2438 new_bitmap->width = new_bitmap->surface->w;
2439 new_bitmap->height = new_bitmap->surface->h;
2441 print_timestamp_done("SDLLoadImage");
2447 /* ------------------------------------------------------------------------- */
2448 /* custom cursor fuctions */
2449 /* ------------------------------------------------------------------------- */
2451 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2453 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2454 cursor_info->width, cursor_info->height,
2455 cursor_info->hot_x, cursor_info->hot_y);
2458 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2460 static struct MouseCursorInfo *last_cursor_info = NULL;
2461 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2462 static SDL_Cursor *cursor_default = NULL;
2463 static SDL_Cursor *cursor_current = NULL;
2465 /* if invoked for the first time, store the SDL default cursor */
2466 if (cursor_default == NULL)
2467 cursor_default = SDL_GetCursor();
2469 /* only create new cursor if cursor info (custom only) has changed */
2470 if (cursor_info != NULL && cursor_info != last_cursor_info)
2472 cursor_current = create_cursor(cursor_info);
2473 last_cursor_info = cursor_info;
2476 /* only set new cursor if cursor info (custom or NULL) has changed */
2477 if (cursor_info != last_cursor_info2)
2478 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2480 last_cursor_info2 = cursor_info;
2484 /* ========================================================================= */
2485 /* audio functions */
2486 /* ========================================================================= */
2488 void SDLOpenAudio(void)
2490 if (program.headless)
2493 #if !defined(TARGET_SDL2)
2494 if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
2495 SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
2498 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2500 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2504 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2505 AUDIO_NUM_CHANNELS_STEREO,
2506 setup.system.audio_fragment_size) < 0)
2508 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2512 audio.sound_available = TRUE;
2513 audio.music_available = TRUE;
2514 audio.loops_available = TRUE;
2515 audio.sound_enabled = TRUE;
2517 /* set number of available mixer channels */
2518 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2519 audio.music_channel = MUSIC_CHANNEL;
2520 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2522 Mixer_InitChannels();
2525 void SDLCloseAudio(void)
2528 Mix_HaltChannel(-1);
2531 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2535 /* ========================================================================= */
2536 /* event functions */
2537 /* ========================================================================= */
2539 void SDLWaitEvent(Event *event)
2541 SDL_WaitEvent(event);
2544 void SDLHandleWindowManagerEvent(Event *event)
2547 #if defined(PLATFORM_WIN32)
2548 // experimental drag and drop code
2550 SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
2551 SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
2553 #if defined(TARGET_SDL2)
2554 if (syswmmsg->msg.win.msg == WM_DROPFILES)
2556 if (syswmmsg->msg == WM_DROPFILES)
2559 #if defined(TARGET_SDL2)
2560 HDROP hdrop = (HDROP)syswmmsg->msg.win.wParam;
2562 HDROP hdrop = (HDROP)syswmmsg->wParam;
2566 printf("::: SDL_SYSWMEVENT:\n");
2568 num_files = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
2570 for (i = 0; i < num_files; i++)
2572 int buffer_len = DragQueryFile(hdrop, i, NULL, 0);
2573 char buffer[buffer_len + 1];
2575 DragQueryFile(hdrop, i, buffer, buffer_len + 1);
2577 printf("::: - '%s'\n", buffer);
2580 #if defined(TARGET_SDL2)
2581 DragFinish((HDROP)syswmmsg->msg.win.wParam);
2583 DragFinish((HDROP)syswmmsg->wParam);
2591 /* ========================================================================= */
2592 /* joystick functions */
2593 /* ========================================================================= */
2595 #if defined(TARGET_SDL2)
2596 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2598 static SDL_Joystick *sdl_joystick[MAX_PLAYERS]; // only joysticks supported
2600 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2601 static int sdl_js_axis[MAX_PLAYERS][2];
2602 static int sdl_js_button[MAX_PLAYERS][2];
2603 static boolean sdl_is_controller[MAX_PLAYERS];
2605 void SDLClearJoystickState()
2609 for (i = 0; i < MAX_PLAYERS; i++)
2611 for (j = 0; j < 2; j++)
2613 sdl_js_axis_raw[i][j] = -1;
2614 sdl_js_axis[i][j] = 0;
2615 sdl_js_button[i][j] = 0;
2620 boolean SDLOpenJoystick(int nr)
2622 if (nr < 0 || nr >= MAX_PLAYERS)
2625 #if defined(TARGET_SDL2)
2626 sdl_is_controller[nr] = SDL_IsGameController(nr);
2628 sdl_is_controller[nr] = FALSE;
2632 Error(ERR_DEBUG, "opening joystick %d (%s)",
2633 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2636 #if defined(TARGET_SDL2)
2637 if (sdl_is_controller[nr])
2638 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2640 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2642 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2645 return (sdl_joystick[nr] != NULL);
2648 void SDLCloseJoystick(int nr)
2650 if (nr < 0 || nr >= MAX_PLAYERS)
2654 Error(ERR_DEBUG, "closing joystick %d", nr);
2657 #if defined(TARGET_SDL2)
2658 if (sdl_is_controller[nr])
2659 SDL_GameControllerClose(sdl_joystick[nr]);
2661 SDL_JoystickClose(sdl_joystick[nr]);
2663 SDL_JoystickClose(sdl_joystick[nr]);
2666 sdl_joystick[nr] = NULL;
2669 boolean SDLCheckJoystickOpened(int nr)
2671 if (nr < 0 || nr >= MAX_PLAYERS)
2674 #if defined(TARGET_SDL2)
2675 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2677 return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
2681 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2683 #if defined(TARGET_SDL2)
2684 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2685 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2686 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2687 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2689 int axis_id = axis_id_raw % 2;
2692 if (nr < 0 || nr >= MAX_PLAYERS)
2698 // prevent (slightly jittering, but centered) axis A from resetting axis B
2699 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2700 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2703 sdl_js_axis[nr][axis_id] = axis_value;
2704 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2707 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2709 #if defined(TARGET_SDL2)
2710 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2711 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2712 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2713 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2714 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2715 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2716 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2717 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2720 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2721 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2722 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2723 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2724 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2725 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2726 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2727 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2729 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2730 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2731 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2732 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2733 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2735 int button_id = button_id_raw % 2;
2738 if (nr < 0 || nr >= MAX_PLAYERS)
2741 if (button_id == -1)
2744 sdl_js_button[nr][button_id] = button_state;
2747 void HandleJoystickEvent(Event *event)
2751 #if defined(TARGET_SDL2)
2752 case SDL_CONTROLLERDEVICEADDED:
2754 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2755 event->cdevice.which);
2760 case SDL_CONTROLLERDEVICEREMOVED:
2762 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2763 event->cdevice.which);
2768 case SDL_CONTROLLERAXISMOTION:
2770 Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2771 event->caxis.which, event->caxis.axis, event->caxis.value);
2773 setJoystickAxis(event->caxis.which,
2775 event->caxis.value);
2778 case SDL_CONTROLLERBUTTONDOWN:
2780 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2781 event->cbutton.which, event->cbutton.button);
2783 setJoystickButton(event->cbutton.which,
2784 event->cbutton.button,
2788 case SDL_CONTROLLERBUTTONUP:
2790 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2791 event->cbutton.which, event->cbutton.button);
2793 setJoystickButton(event->cbutton.which,
2794 event->cbutton.button,
2799 case SDL_JOYAXISMOTION:
2800 if (sdl_is_controller[event->jaxis.which])
2804 Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2805 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2807 if (event->jaxis.axis < 4)
2808 setJoystickAxis(event->jaxis.which,
2810 event->jaxis.value);
2813 case SDL_JOYBUTTONDOWN:
2814 if (sdl_is_controller[event->jaxis.which])
2818 Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2819 event->jbutton.which, event->jbutton.button);
2821 if (event->jbutton.button < 4)
2822 setJoystickButton(event->jbutton.which,
2823 event->jbutton.button,
2827 case SDL_JOYBUTTONUP:
2828 if (sdl_is_controller[event->jaxis.which])
2832 Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2833 event->jbutton.which, event->jbutton.button);
2835 if (event->jbutton.button < 4)
2836 setJoystickButton(event->jbutton.which,
2837 event->jbutton.button,
2846 void SDLInitJoysticks()
2848 static boolean sdl_joystick_subsystem_initialized = FALSE;
2849 boolean print_warning = !sdl_joystick_subsystem_initialized;
2850 #if defined(TARGET_SDL2)
2851 char *mappings_file_base = getPath2(options.conf_directory,
2852 GAMECONTROLLER_BASENAME);
2853 char *mappings_file_user = getPath2(getUserGameDataDir(),
2854 GAMECONTROLLER_BASENAME);
2859 if (!sdl_joystick_subsystem_initialized)
2861 sdl_joystick_subsystem_initialized = TRUE;
2863 #if defined(TARGET_SDL2)
2864 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2866 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2868 if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
2871 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2875 #if defined(TARGET_SDL2)
2876 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2878 /* the included game controller base mappings should always be found */
2879 if (num_mappings == -1)
2880 Error(ERR_WARN, "no game controller base mappings found");
2883 Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2886 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2889 /* the personal game controller user mappings may or may not be found */
2890 if (num_mappings == -1)
2891 Error(ERR_WARN, "no game controller user mappings found");
2893 Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2895 Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2898 checked_free(mappings_file_base);
2899 checked_free(mappings_file_user);
2902 for (i = 0; i < SDL_NumJoysticks(); i++)
2904 const char *name, *type;
2906 if (SDL_IsGameController(i))
2908 name = SDL_GameControllerNameForIndex(i);
2909 type = "game controller";
2913 name = SDL_JoystickNameForIndex(i);
2917 Error(ERR_INFO, "- joystick %d (%s): '%s'",
2918 i, type, (name ? name : "(Unknown)"));
2924 /* assign joysticks from configured to connected joystick for all players */
2925 for (i = 0; i < MAX_PLAYERS; i++)
2927 /* get configured joystick for this player */
2928 char *device_name = setup.input[i].joy.device_name;
2929 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2931 if (joystick_nr >= SDL_NumJoysticks())
2933 if (setup.input[i].use_joystick && print_warning)
2934 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2939 /* store configured joystick number for each player */
2940 joystick.nr[i] = joystick_nr;
2943 /* now open all connected joysticks (regardless if configured or not) */
2944 for (i = 0; i < SDL_NumJoysticks(); i++)
2946 /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
2947 if (SDLCheckJoystickOpened(i))
2948 SDLCloseJoystick(i);
2950 if (SDLOpenJoystick(i))
2951 joystick.status = JOYSTICK_ACTIVATED;
2952 else if (print_warning)
2953 Error(ERR_WARN, "cannot open joystick %d", i);
2956 SDLClearJoystickState();
2959 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2961 if (nr < 0 || nr >= MAX_PLAYERS)
2965 *x = sdl_js_axis[nr][0];
2967 *y = sdl_js_axis[nr][1];
2970 *b1 = sdl_js_button[nr][0];
2972 *b2 = sdl_js_button[nr][1];
2978 /* ========================================================================= */
2979 /* touch input overlay functions */
2980 /* ========================================================================= */
2982 #if defined(USE_TOUCH_INPUT_OVERLAY)
2983 static void DrawTouchInputOverlay()
2985 static SDL_Texture *texture = NULL;
2986 static boolean initialized = FALSE;
2987 static boolean deactivated = TRUE;
2988 static int width = 0, height = 0;
2989 static int alpha_max = SDL_ALPHA_OPAQUE / 2;
2990 static int alpha_step = 5;
2991 static int alpha_last = 0;
2992 static int alpha = 0;
2993 boolean active = (overlay.enabled && overlay.active);
2995 if (!active && deactivated)
3000 if (alpha < alpha_max)
3001 alpha = MIN(alpha + alpha_step, alpha_max);
3003 deactivated = FALSE;
3007 alpha = MAX(0, alpha - alpha_step);
3015 char *basename = "overlay/VirtualButtons.png";
3016 char *filename = getCustomImageFilename(basename);
3018 if (filename == NULL)
3019 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
3021 SDL_Surface *surface;
3023 if ((surface = IMG_Load(filename)) == NULL)
3024 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
3027 height = surface->h;
3029 /* set black pixel to transparent if no alpha channel / transparent color */
3030 if (!SDLHasAlpha(surface) &&
3031 !SDLHasColorKey(surface))
3032 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
3033 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
3035 if ((texture = SDLCreateTextureFromSurface(surface)) == NULL)
3036 Error(ERR_EXIT, "SDLCreateTextureFromSurface() failed");
3038 SDL_FreeSurface(surface);
3040 SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
3041 SDL_SetTextureAlphaMod(texture, alpha_max);
3046 if (alpha != alpha_last)
3047 SDL_SetTextureAlphaMod(texture, alpha);
3051 float ratio_overlay = (float) width / height;
3052 float ratio_screen = (float) video.screen_width / video.screen_height;
3053 int width_scaled, height_scaled;
3056 if (ratio_overlay > ratio_screen)
3058 width_scaled = video.screen_width;
3059 height_scaled = video.screen_height * ratio_screen / ratio_overlay;
3061 ypos = video.screen_height - height_scaled;
3065 width_scaled = video.screen_width * ratio_overlay / ratio_screen;
3066 height_scaled = video.screen_height;
3067 xpos = (video.screen_width - width_scaled) / 2;
3071 SDL_Rect src_rect = { 0, 0, width, height };
3072 SDL_Rect dst_rect = { xpos, ypos, width_scaled, height_scaled };
3074 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);