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);
66 // copy tile selection cursor to render target buffer, if defined (above all)
67 if (gfx.draw_tile_cursor_function != NULL)
68 gfx.draw_tile_cursor_function(draw_target);
71 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
73 static unsigned int update_screen_delay = 0;
74 unsigned int update_screen_delay_value = 50; // (milliseconds)
75 SDL_Surface *screen = backbuffer->surface;
77 if (limit_screen_updates &&
78 !DelayReached(&update_screen_delay, update_screen_delay_value))
81 LimitScreenUpdates(FALSE);
85 static int LastFrameCounter = 0;
86 boolean changed = (FrameCounter != LastFrameCounter);
88 printf("::: FrameCounter == %d [%s]\n", FrameCounter,
89 (changed ? "-" : "SAME FRAME UPDATED"));
91 LastFrameCounter = FrameCounter;
100 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
101 gfx.final_screen_bitmap != NULL) // may not be initialized yet
103 // draw global animations using bitmaps instead of using textures
104 // to prevent texture scaling artefacts (this is potentially slower)
106 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
107 gfx.win_xsize, gfx.win_ysize, 0, 0);
109 FinalizeScreen(DRAW_TO_SCREEN);
111 screen = gfx.final_screen_bitmap->surface;
113 // force full window redraw
117 #if defined(TARGET_SDL2)
118 SDL_Texture *sdl_texture = sdl_texture_stream;
120 // deactivate use of target texture if render targets are not supported
121 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
122 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
123 sdl_texture_target == NULL)
124 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
126 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
127 sdl_texture = sdl_texture_target;
131 int bytes_x = screen->pitch / video.width;
132 int bytes_y = screen->pitch;
134 SDL_UpdateTexture(sdl_texture, rect,
135 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
140 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
143 int xoff = video.screen_xoffset;
144 int yoff = video.screen_yoffset;
145 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
146 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
147 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
149 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
150 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
151 dst_rect2 = &dst_rect_screen;
153 dst_rect1 = &dst_rect_screen;
155 #if defined(HAS_SCREEN_KEYBOARD)
156 if (video.shifted_up || video.shifted_up_delay)
158 int time_current = SDL_GetTicks();
159 int pos = video.shifted_up_pos;
160 int pos_last = video.shifted_up_pos_last;
162 if (!DelayReachedExt(&video.shifted_up_delay, video.shifted_up_delay_value,
165 int delay = time_current - video.shifted_up_delay;
166 int delay_value = video.shifted_up_delay_value;
168 pos = pos_last + (pos - pos_last) * delay / delay_value;
172 video.shifted_up_pos_last = pos;
173 video.shifted_up_delay = 0;
176 SDL_Rect src_rect_up = { 0, pos, video.width, video.height - pos };
177 SDL_Rect dst_rect_up = { xoff, yoff, video.width, video.height - pos };
179 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
180 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
182 src_rect2 = &src_rect_up;
183 dst_rect2 = &dst_rect_up;
187 src_rect1 = &src_rect_up;
188 dst_rect1 = &dst_rect_up;
193 // clear render target buffer
194 SDL_RenderClear(sdl_renderer);
196 // set renderer to use target texture for rendering
197 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
198 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
199 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
201 // copy backbuffer texture to render target buffer
202 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
203 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
205 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
206 FinalizeScreen(DRAW_TO_SCREEN);
208 // when using target texture, copy it to screen buffer
209 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
210 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
212 SDL_SetRenderTarget(sdl_renderer, NULL);
213 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
216 #if defined(USE_TOUCH_INPUT_OVERLAY)
217 // draw overlay graphics for touch device input, if needed
218 DrawTouchInputOverlay();
223 // global synchronization point of the game to align video frame delay
224 if (with_frame_delay)
225 WaitUntilDelayReached(&video.frame_delay, video.frame_delay_value);
227 #if defined(TARGET_SDL2)
228 // show render target buffer on screen
229 SDL_RenderPresent(sdl_renderer);
232 SDL_UpdateRects(screen, 1, rect);
234 SDL_UpdateRect(screen, 0, 0, 0, 0);
238 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
240 UpdateScreenExt(rect, TRUE);
243 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
245 UpdateScreenExt(rect, FALSE);
248 void Delay_WithScreenUpdates(unsigned int delay)
250 unsigned int time_current = SDL_GetTicks();
251 unsigned int time_delayed = time_current + delay;
253 while (time_current < time_delayed)
255 // updating the screen contains waiting for frame delay (non-busy)
256 UpdateScreen_WithFrameDelay(NULL);
258 time_current = SDL_GetTicks();
262 static void SDLSetWindowIcon(char *basename)
264 // (setting the window icon on Mac OS X would replace the high-quality
265 // dock icon with the currently smaller (and uglier) icon from file)
267 #if !defined(PLATFORM_MACOSX)
268 char *filename = getCustomImageFilename(basename);
269 SDL_Surface *surface;
271 if (filename == NULL)
273 Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
278 if ((surface = IMG_Load(filename)) == NULL)
280 Error(ERR_WARN, "IMG_Load() failed: %s", SDL_GetError());
285 // set transparent color
286 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
287 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
289 #if defined(TARGET_SDL2)
290 SDL_SetWindowIcon(sdl_window, surface);
292 SDL_WM_SetIcon(surface, NULL);
297 #if defined(TARGET_SDL2)
299 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
300 SDL_PixelFormat *format2)
302 return (format1->format == format2->format &&
303 format1->BitsPerPixel == format2->BitsPerPixel &&
304 format1->BytesPerPixel == format2->BytesPerPixel &&
305 format1->Rmask == format2->Rmask &&
306 format1->Gmask == format2->Gmask &&
307 format1->Bmask == format2->Bmask);
310 static Pixel SDLGetColorKey(SDL_Surface *surface)
314 if (SDL_GetColorKey(surface, &color_key) != 0)
320 static boolean SDLHasColorKey(SDL_Surface *surface)
322 return (SDLGetColorKey(surface) != -1);
325 static boolean SDLHasAlpha(SDL_Surface *surface)
327 SDL_BlendMode blend_mode;
329 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
332 return (blend_mode == SDL_BLENDMODE_BLEND);
335 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
337 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
339 SDL_SetSurfaceBlendMode(surface, blend_mode);
340 SDL_SetSurfaceAlphaMod(surface, alpha);
343 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
345 SDL_PixelFormat format;
346 SDL_Surface *new_surface;
351 if (backbuffer && backbuffer->surface)
353 format = *backbuffer->surface->format;
354 format.Amask = surface->format->Amask; // keep alpha channel
358 format = *surface->format;
361 new_surface = SDL_ConvertSurface(surface, &format, 0);
363 if (new_surface == NULL)
364 Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
369 boolean SDLSetNativeSurface(SDL_Surface **surface)
371 SDL_Surface *new_surface;
373 if (surface == NULL ||
375 backbuffer == NULL ||
376 backbuffer->surface == NULL)
379 // if pixel format already optimized for destination surface, do nothing
380 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
383 new_surface = SDLGetNativeSurface(*surface);
385 SDL_FreeSurface(*surface);
387 *surface = new_surface;
394 static Pixel SDLGetColorKey(SDL_Surface *surface)
396 if ((surface->flags & SDL_SRCCOLORKEY) == 0)
399 return surface->format->colorkey;
402 static boolean SDLHasColorKey(SDL_Surface *surface)
404 return (SDLGetColorKey(surface) != -1);
407 static boolean SDLHasAlpha(SDL_Surface *surface)
409 return ((surface->flags & SDL_SRCALPHA) != 0);
412 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
414 SDL_SetAlpha(surface, (set ? SDL_SRCALPHA : 0), alpha);
417 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
419 SDL_Surface *new_surface;
424 if (!video.initialized)
425 new_surface = SDL_ConvertSurface(surface, surface->format, SURFACE_FLAGS);
426 else if (SDLHasAlpha(surface))
427 new_surface = SDL_DisplayFormatAlpha(surface);
429 new_surface = SDL_DisplayFormat(surface);
431 if (new_surface == NULL)
432 Error(ERR_EXIT, "%s() failed: %s",
433 (video.initialized ? "SDL_DisplayFormat" : "SDL_ConvertSurface"),
439 boolean SDLSetNativeSurface(SDL_Surface **surface)
441 SDL_Surface *new_surface;
443 if (surface == NULL ||
448 new_surface = SDLGetNativeSurface(*surface);
450 SDL_FreeSurface(*surface);
452 *surface = new_surface;
459 #if defined(TARGET_SDL2)
460 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
462 if (program.headless)
465 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
468 Error(ERR_EXIT, "SDL_CreateTextureFromSurface() failed: %s",
475 void SDLCreateBitmapTextures(Bitmap *bitmap)
477 #if defined(TARGET_SDL2)
482 SDL_DestroyTexture(bitmap->texture);
483 if (bitmap->texture_masked)
484 SDL_DestroyTexture(bitmap->texture_masked);
486 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
487 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
491 void SDLFreeBitmapTextures(Bitmap *bitmap)
493 #if defined(TARGET_SDL2)
498 SDL_DestroyTexture(bitmap->texture);
499 if (bitmap->texture_masked)
500 SDL_DestroyTexture(bitmap->texture_masked);
502 bitmap->texture = NULL;
503 bitmap->texture_masked = NULL;
507 void SDLInitVideoDisplay(void)
509 #if !defined(TARGET_SDL2)
510 if (!strEqual(setup.system.sdl_videodriver, ARG_DEFAULT))
511 SDL_putenv(getStringCat2("SDL_VIDEODRIVER=", setup.system.sdl_videodriver));
513 SDL_putenv("SDL_VIDEO_CENTERED=1");
516 // initialize SDL video
517 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
518 Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
520 // set default SDL depth
521 #if !defined(TARGET_SDL2)
522 video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
524 video.default_depth = 32; // (how to determine video depth in SDL2?)
528 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
530 if (program.headless)
533 video.window_scaling_percent = setup.window_scaling_percent;
534 video.window_scaling_quality = setup.window_scaling_quality;
536 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
538 #if defined(TARGET_SDL2)
539 // SDL 2.0: support for (desktop) fullscreen mode available
540 video.fullscreen_available = TRUE;
542 // SDL 1.2: no support for fullscreen mode in R'n'D anymore
543 video.fullscreen_available = FALSE;
546 // open SDL video output device (window or fullscreen mode)
547 if (!SDLSetVideoMode(fullscreen))
548 Error(ERR_EXIT, "setting video mode failed");
550 // !!! SDL2 can only set the window icon if the window already exists !!!
552 SDLSetWindowIcon(program.icon_filename);
554 // set window and icon title
558 static void SDLInitVideoBuffer_DrawBuffer(void)
560 /* SDL cannot directly draw to the visible video framebuffer like X11,
561 but always uses a backbuffer, which is then blitted to the visible
562 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
563 visible video framebuffer with 'SDL_Flip', if the hardware supports
564 this). Therefore do not use an additional backbuffer for drawing, but
565 use a symbolic buffer (distinguishable from the SDL backbuffer) called
566 'window', which indicates that the SDL backbuffer should be updated to
567 the visible video framebuffer when attempting to blit to it.
569 For convenience, it seems to be a good idea to create this symbolic
570 buffer 'window' at the same size as the SDL backbuffer. Although it
571 should never be drawn to directly, it would do no harm nevertheless. */
573 // create additional (symbolic) buffer for double-buffering
574 ReCreateBitmap(&window, video.width, video.height);
576 // create dummy drawing buffer for headless mode, if needed
577 if (program.headless)
578 ReCreateBitmap(&backbuffer, video.width, video.height);
581 void SDLInitVideoBuffer(boolean fullscreen)
583 SDLInitVideoBuffer_VideoBuffer(fullscreen);
584 SDLInitVideoBuffer_DrawBuffer();
587 static boolean SDLCreateScreen(boolean fullscreen)
589 SDL_Surface *new_surface = NULL;
591 #if defined(TARGET_SDL2)
592 int surface_flags_window = SURFACE_FLAGS | SDL_WINDOW_RESIZABLE;
593 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
595 int surface_flags_window = SURFACE_FLAGS;
596 int surface_flags_fullscreen = SURFACE_FLAGS; // (no fullscreen in SDL 1.2)
599 #if defined(TARGET_SDL2)
601 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
603 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
604 _without_ enabling 2D/3D acceleration and/or guest additions installed,
605 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
606 it will try to use accelerated graphics and apparently fails miserably) */
607 int renderer_flags = SDL_RENDERER_SOFTWARE;
610 SDLSetScreenSizeAndOffsets(video.width, video.height);
613 int width = video.width;
614 int height = video.height;
615 int screen_width = video.screen_width;
616 int screen_height = video.screen_height;
617 int surface_flags = (fullscreen ? surface_flags_fullscreen :
618 surface_flags_window);
620 // default window size is unscaled
621 video.window_width = screen_width;
622 video.window_height = screen_height;
624 #if defined(TARGET_SDL2)
626 // store if initial screen mode is fullscreen mode when changing screen size
627 video.fullscreen_initial = fullscreen;
629 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
631 video.window_width = window_scaling_factor * screen_width;
632 video.window_height = window_scaling_factor * screen_height;
634 if (sdl_texture_stream)
636 SDL_DestroyTexture(sdl_texture_stream);
637 sdl_texture_stream = NULL;
640 if (sdl_texture_target)
642 SDL_DestroyTexture(sdl_texture_target);
643 sdl_texture_target = NULL;
646 if (!(fullscreen && fullscreen_enabled))
650 SDL_DestroyRenderer(sdl_renderer);
656 SDL_DestroyWindow(sdl_window);
661 if (sdl_window == NULL)
662 sdl_window = SDL_CreateWindow(program.window_title,
663 SDL_WINDOWPOS_CENTERED,
664 SDL_WINDOWPOS_CENTERED,
669 if (sdl_window != NULL)
671 if (sdl_renderer == NULL)
672 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
674 if (sdl_renderer != NULL)
676 SDL_RenderSetLogicalSize(sdl_renderer, screen_width, screen_height);
677 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
678 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
680 SDLSetScreenVsyncMode(setup.vsync_mode);
682 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
683 SDL_PIXELFORMAT_ARGB8888,
684 SDL_TEXTUREACCESS_STREAMING,
687 if (SDL_RenderTargetSupported(sdl_renderer))
688 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
689 SDL_PIXELFORMAT_ARGB8888,
690 SDL_TEXTUREACCESS_TARGET,
693 if (sdl_texture_stream != NULL)
695 // use SDL default values for RGB masks and no alpha channel
696 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
698 if (new_surface == NULL)
699 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
703 Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
708 Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
713 Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
718 if (gfx.final_screen_bitmap == NULL)
719 gfx.final_screen_bitmap = CreateBitmapStruct();
721 gfx.final_screen_bitmap->width = width;
722 gfx.final_screen_bitmap->height = height;
724 gfx.final_screen_bitmap->surface =
725 SDL_SetVideoMode(width, height, video.depth, surface_flags);
727 if (gfx.final_screen_bitmap->surface != NULL)
730 SDL_CreateRGBSurface(surface_flags, width, height, video.depth, 0,0,0, 0);
732 if (new_surface == NULL)
733 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
736 new_surface = gfx.final_screen_bitmap->surface;
737 gfx.final_screen_bitmap = NULL;
743 Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
747 #if defined(TARGET_SDL2)
748 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
749 if (new_surface != NULL)
750 fullscreen_enabled = fullscreen;
753 if (backbuffer == NULL)
754 backbuffer = CreateBitmapStruct();
756 backbuffer->width = video.width;
757 backbuffer->height = video.height;
759 if (backbuffer->surface)
760 SDL_FreeSurface(backbuffer->surface);
762 backbuffer->surface = new_surface;
764 return (new_surface != NULL);
767 boolean SDLSetVideoMode(boolean fullscreen)
769 boolean success = FALSE;
773 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
775 // switch display to fullscreen mode, if available
776 success = SDLCreateScreen(TRUE);
780 // switching display to fullscreen mode failed -- do not try it again
781 video.fullscreen_available = FALSE;
785 video.fullscreen_enabled = TRUE;
789 if ((!fullscreen && video.fullscreen_enabled) || !success)
791 // switch display to window mode
792 success = SDLCreateScreen(FALSE);
796 // switching display to window mode failed -- should not happen
800 video.fullscreen_enabled = FALSE;
801 video.window_scaling_percent = setup.window_scaling_percent;
802 video.window_scaling_quality = setup.window_scaling_quality;
804 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
808 #if defined(TARGET_SDL2)
809 SDLRedrawWindow(); // map window
813 #if defined(PLATFORM_WIN32)
814 // experimental drag and drop code
816 SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
819 SDL_SysWMinfo wminfo;
821 boolean wminfo_success = FALSE;
823 SDL_VERSION(&wminfo.version);
824 #if defined(TARGET_SDL2)
826 wminfo_success = SDL_GetWindowWMInfo(sdl_window, &wminfo);
828 wminfo_success = (SDL_GetWMInfo(&wminfo) == 1);
833 #if defined(TARGET_SDL2)
834 hwnd = wminfo.info.win.window;
836 hwnd = wminfo.window;
839 DragAcceptFiles(hwnd, TRUE);
848 void SDLSetWindowTitle(void)
850 #if defined(TARGET_SDL2)
851 if (sdl_window == NULL)
854 SDL_SetWindowTitle(sdl_window, program.window_title);
856 SDL_WM_SetCaption(program.window_title, program.window_title);
860 #if defined(TARGET_SDL2)
861 void SDLSetWindowScaling(int window_scaling_percent)
863 if (sdl_window == NULL)
866 float window_scaling_factor = (float)window_scaling_percent / 100;
867 int new_window_width = (int)(window_scaling_factor * video.screen_width);
868 int new_window_height = (int)(window_scaling_factor * video.screen_height);
870 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
872 video.window_scaling_percent = window_scaling_percent;
873 video.window_width = new_window_width;
874 video.window_height = new_window_height;
879 void SDLSetWindowScalingQuality(char *window_scaling_quality)
881 SDL_Texture *new_texture;
883 if (sdl_texture_stream == NULL)
886 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
888 new_texture = SDL_CreateTexture(sdl_renderer,
889 SDL_PIXELFORMAT_ARGB8888,
890 SDL_TEXTUREACCESS_STREAMING,
891 video.width, video.height);
893 if (new_texture != NULL)
895 SDL_DestroyTexture(sdl_texture_stream);
897 sdl_texture_stream = new_texture;
900 if (SDL_RenderTargetSupported(sdl_renderer))
901 new_texture = SDL_CreateTexture(sdl_renderer,
902 SDL_PIXELFORMAT_ARGB8888,
903 SDL_TEXTUREACCESS_TARGET,
904 video.width, video.height);
908 if (new_texture != NULL)
910 SDL_DestroyTexture(sdl_texture_target);
912 sdl_texture_target = new_texture;
917 video.window_scaling_quality = window_scaling_quality;
920 void SDLSetWindowFullscreen(boolean fullscreen)
922 if (sdl_window == NULL)
925 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
927 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
928 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
930 // if screen size was changed in fullscreen mode, correct desktop window size
931 if (!fullscreen && video.fullscreen_initial)
933 SDLSetWindowScaling(setup.window_scaling_percent);
934 SDL_SetWindowPosition(sdl_window,
935 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
937 video.fullscreen_initial = FALSE;
941 void SDLSetDisplaySize(void)
943 SDL_Rect display_bounds;
945 SDL_GetDisplayBounds(0, &display_bounds);
947 video.display_width = display_bounds.w;
948 video.display_height = display_bounds.h;
951 Error(ERR_DEBUG, "SDL real screen size: %d x %d",
952 video.display_width, video.display_height);
956 void SDLSetScreenSizeAndOffsets(int width, int height)
958 // set default video screen size and offsets
959 video.screen_width = width;
960 video.screen_height = height;
961 video.screen_xoffset = 0;
962 video.screen_yoffset = 0;
964 #if defined(USE_COMPLETE_DISPLAY)
965 float ratio_video = (float) width / height;
966 float ratio_display = (float) video.display_width / video.display_height;
968 if (ratio_video != ratio_display)
970 // adjust drawable screen size to cover the whole device display
972 if (ratio_video < ratio_display)
973 video.screen_width *= ratio_display / ratio_video;
975 video.screen_height *= ratio_video / ratio_display;
977 video.screen_xoffset = (video.screen_width - width) / 2;
978 video.screen_yoffset = (video.screen_height - height) / 2;
981 Error(ERR_DEBUG, "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
983 video.screen_width, video.screen_height,
984 ratio_video, ratio_display);
990 void SDLSetScreenSizeForRenderer(int width, int height)
992 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
995 void SDLSetScreenProperties(void)
997 SDLSetScreenSizeAndOffsets(video.width, video.height);
998 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
1003 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
1005 #if defined(TARGET_SDL2)
1006 video.screen_rendering_mode =
1007 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
1008 SPECIAL_RENDERING_BITMAP :
1009 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
1010 SPECIAL_RENDERING_TARGET:
1011 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
1012 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
1014 video.screen_rendering_mode = SPECIAL_RENDERING_BITMAP;
1018 void SDLSetScreenVsyncMode(char *vsync_mode)
1020 #if defined(TARGET_SDL2)
1022 (strEqual(vsync_mode, STR_VSYNC_MODE_NORMAL) ? VSYNC_MODE_NORMAL :
1023 strEqual(vsync_mode, STR_VSYNC_MODE_ADAPTIVE) ? VSYNC_MODE_ADAPTIVE :
1025 int result = SDL_GL_SetSwapInterval(interval);
1027 // if adaptive vsync requested, but not supported, retry with normal vsync
1028 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
1029 SDL_GL_SetSwapInterval(VSYNC_MODE_NORMAL);
1033 void SDLRedrawWindow(void)
1035 UpdateScreen_WithoutFrameDelay(NULL);
1038 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
1041 if (program.headless)
1044 SDL_Surface *surface =
1045 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
1047 if (surface == NULL)
1048 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
1050 SDLSetNativeSurface(&surface);
1052 bitmap->surface = surface;
1055 void SDLFreeBitmapPointers(Bitmap *bitmap)
1057 if (bitmap->surface)
1058 SDL_FreeSurface(bitmap->surface);
1059 if (bitmap->surface_masked)
1060 SDL_FreeSurface(bitmap->surface_masked);
1062 bitmap->surface = NULL;
1063 bitmap->surface_masked = NULL;
1065 #if defined(TARGET_SDL2)
1066 if (bitmap->texture)
1067 SDL_DestroyTexture(bitmap->texture);
1068 if (bitmap->texture_masked)
1069 SDL_DestroyTexture(bitmap->texture_masked);
1071 bitmap->texture = NULL;
1072 bitmap->texture_masked = NULL;
1076 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1077 int src_x, int src_y, int width, int height,
1078 int dst_x, int dst_y, int mask_mode)
1080 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1081 SDL_Rect src_rect, dst_rect;
1086 src_rect.h = height;
1091 dst_rect.h = height;
1093 // if (src_bitmap != backbuffer || dst_bitmap != window)
1094 if (!(src_bitmap == backbuffer && dst_bitmap == window))
1095 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
1096 src_bitmap->surface_masked : src_bitmap->surface),
1097 &src_rect, real_dst_bitmap->surface, &dst_rect);
1099 if (dst_bitmap == window)
1100 UpdateScreen_WithFrameDelay(&dst_rect);
1103 void SDLBlitTexture(Bitmap *bitmap,
1104 int src_x, int src_y, int width, int height,
1105 int dst_x, int dst_y, int mask_mode)
1107 #if defined(TARGET_SDL2)
1108 SDL_Texture *texture;
1113 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1115 if (texture == NULL)
1121 src_rect.h = height;
1126 dst_rect.h = height;
1128 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1132 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1135 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1143 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1145 if (dst_bitmap == window)
1146 UpdateScreen_WithFrameDelay(&rect);
1149 void PrepareFadeBitmap(int draw_target)
1151 Bitmap *fade_bitmap =
1152 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1153 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1155 if (fade_bitmap == NULL)
1158 // copy backbuffer to fading buffer
1159 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1161 // add border and animations to fading buffer
1162 FinalizeScreen(draw_target);
1165 void SDLFadeRectangle(int x, int y, int width, int height,
1166 int fade_mode, int fade_delay, int post_delay,
1167 void (*draw_border_function)(void))
1169 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1170 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1171 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1172 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1173 SDL_Surface *surface_screen = backbuffer->surface;
1174 SDL_Rect src_rect, dst_rect;
1176 int src_x = x, src_y = y;
1177 int dst_x = x, dst_y = y;
1178 unsigned int time_last, time_current;
1180 // store function for drawing global masked border
1181 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1183 // deactivate drawing of global border while fading, if needed
1184 if (draw_border_function == NULL)
1185 gfx.draw_global_border_function = NULL;
1190 src_rect.h = height;
1194 dst_rect.w = width; // (ignored)
1195 dst_rect.h = height; // (ignored)
1197 dst_rect2 = dst_rect;
1199 // before fading in, store backbuffer (without animation graphics)
1200 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1201 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1203 // copy source and target surfaces to temporary surfaces for fading
1204 if (fade_mode & FADE_TYPE_TRANSFORM)
1206 // (source and target fading buffer already prepared)
1208 else if (fade_mode & FADE_TYPE_FADE_IN)
1210 // (target fading buffer already prepared)
1211 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1213 else // FADE_TYPE_FADE_OUT
1215 // (source fading buffer already prepared)
1216 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1219 time_current = SDL_GetTicks();
1221 if (fade_mode == FADE_MODE_MELT)
1223 boolean done = FALSE;
1224 int melt_pixels = 2;
1225 int melt_columns = width / melt_pixels;
1226 int ypos[melt_columns];
1227 int max_steps = height / 8 + 32;
1232 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1234 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1236 ypos[0] = -GetSimpleRandom(16);
1238 for (i = 1 ; i < melt_columns; i++)
1240 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1242 ypos[i] = ypos[i - 1] + r;
1255 time_last = time_current;
1256 time_current = SDL_GetTicks();
1257 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1258 steps_final = MIN(MAX(0, steps), max_steps);
1262 done = (steps_done >= steps_final);
1264 for (i = 0 ; i < melt_columns; i++)
1272 else if (ypos[i] < height)
1277 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1279 if (ypos[i] + dy >= height)
1280 dy = height - ypos[i];
1282 // copy part of (appearing) target surface to upper area
1283 src_rect.x = src_x + i * melt_pixels;
1284 // src_rect.y = src_y + ypos[i];
1286 src_rect.w = melt_pixels;
1288 src_rect.h = ypos[i] + dy;
1290 dst_rect.x = dst_x + i * melt_pixels;
1291 // dst_rect.y = dst_y + ypos[i];
1294 if (steps_done >= steps_final)
1295 SDL_BlitSurface(surface_target, &src_rect,
1296 surface_screen, &dst_rect);
1300 // copy part of (disappearing) source surface to lower area
1301 src_rect.x = src_x + i * melt_pixels;
1303 src_rect.w = melt_pixels;
1304 src_rect.h = height - ypos[i];
1306 dst_rect.x = dst_x + i * melt_pixels;
1307 dst_rect.y = dst_y + ypos[i];
1309 if (steps_done >= steps_final)
1310 SDL_BlitSurface(surface_source, &src_rect,
1311 surface_screen, &dst_rect);
1317 src_rect.x = src_x + i * melt_pixels;
1319 src_rect.w = melt_pixels;
1320 src_rect.h = height;
1322 dst_rect.x = dst_x + i * melt_pixels;
1325 if (steps_done >= steps_final)
1326 SDL_BlitSurface(surface_target, &src_rect,
1327 surface_screen, &dst_rect);
1331 if (steps_done >= steps_final)
1333 if (draw_border_function != NULL)
1334 draw_border_function();
1336 UpdateScreen_WithFrameDelay(&dst_rect2);
1340 else if (fade_mode == FADE_MODE_CURTAIN)
1344 int xx_size = width / 2;
1346 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1348 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1350 for (xx = 0; xx < xx_size;)
1352 time_last = time_current;
1353 time_current = SDL_GetTicks();
1354 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1355 xx_final = MIN(MAX(0, xx), xx_size);
1360 src_rect.h = height;
1365 // draw new (target) image to screen buffer
1366 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1368 if (xx_final < xx_size)
1370 src_rect.w = xx_size - xx_final;
1371 src_rect.h = height;
1373 // draw old (source) image to screen buffer (left side)
1375 src_rect.x = src_x + xx_final;
1378 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1380 // draw old (source) image to screen buffer (right side)
1382 src_rect.x = src_x + xx_size;
1383 dst_rect.x = dst_x + xx_size + xx_final;
1385 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1388 if (draw_border_function != NULL)
1389 draw_border_function();
1391 // only update the region of the screen that is affected from fading
1392 UpdateScreen_WithFrameDelay(&dst_rect2);
1395 else // fading in, fading out or cross-fading
1400 for (alpha = 0.0; alpha < 255.0;)
1402 time_last = time_current;
1403 time_current = SDL_GetTicks();
1404 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1405 alpha_final = MIN(MAX(0, alpha), 255);
1407 // draw existing (source) image to screen buffer
1408 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1410 // draw new (target) image to screen buffer using alpha blending
1411 SDLSetAlpha(surface_target, TRUE, alpha_final);
1412 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1414 if (draw_border_function != NULL)
1415 draw_border_function();
1417 // only update the region of the screen that is affected from fading
1418 UpdateScreen_WithFrameDelay(&dst_rect);
1423 Delay_WithScreenUpdates(post_delay);
1425 // restore function for drawing global masked border
1426 gfx.draw_global_border_function = draw_global_border_function;
1428 // after fading in, restore backbuffer (without animation graphics)
1429 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1430 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1433 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1434 int to_x, int to_y, Uint32 color)
1436 SDL_Surface *surface = dst_bitmap->surface;
1440 swap_numbers(&from_x, &to_x);
1443 swap_numbers(&from_y, &to_y);
1447 rect.w = (to_x - from_x + 1);
1448 rect.h = (to_y - from_y + 1);
1450 SDL_FillRect(surface, &rect, color);
1453 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1454 int to_x, int to_y, Uint32 color)
1456 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1459 #if ENABLE_UNUSED_CODE
1460 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1461 int num_points, Uint32 color)
1466 for (i = 0; i < num_points - 1; i++)
1468 for (x = 0; x < line_width; x++)
1470 for (y = 0; y < line_width; y++)
1472 int dx = x - line_width / 2;
1473 int dy = y - line_width / 2;
1475 if ((x == 0 && y == 0) ||
1476 (x == 0 && y == line_width - 1) ||
1477 (x == line_width - 1 && y == 0) ||
1478 (x == line_width - 1 && y == line_width - 1))
1481 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1482 points[i+1].x + dx, points[i+1].y + dy, color);
1489 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1491 SDL_Surface *surface = src_bitmap->surface;
1493 switch (surface->format->BytesPerPixel)
1495 case 1: // assuming 8-bpp
1497 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1501 case 2: // probably 15-bpp or 16-bpp
1503 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1507 case 3: // slow 24-bpp mode; usually not used
1510 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1514 shift = surface->format->Rshift;
1515 color |= *(pix + shift / 8) >> shift;
1516 shift = surface->format->Gshift;
1517 color |= *(pix + shift / 8) >> shift;
1518 shift = surface->format->Bshift;
1519 color |= *(pix + shift / 8) >> shift;
1525 case 4: // probably 32-bpp
1527 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1536 // ============================================================================
1537 // The following functions were taken from the SGE library
1538 // (SDL Graphics Extension Library) by Anders Lindström
1539 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1540 // ============================================================================
1542 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1544 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1546 switch (surface->format->BytesPerPixel)
1551 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1557 // Probably 15-bpp or 16-bpp
1558 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1564 // Slow 24-bpp mode, usually not used
1568 // Gack - slow, but endian correct
1569 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1570 shift = surface->format->Rshift;
1571 *(pix+shift/8) = color>>shift;
1572 shift = surface->format->Gshift;
1573 *(pix+shift/8) = color>>shift;
1574 shift = surface->format->Bshift;
1575 *(pix+shift/8) = color>>shift;
1582 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1590 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1591 Uint8 R, Uint8 G, Uint8 B)
1593 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1596 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1598 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1601 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1603 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1606 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1611 // Gack - slow, but endian correct
1612 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1613 shift = surface->format->Rshift;
1614 *(pix+shift/8) = color>>shift;
1615 shift = surface->format->Gshift;
1616 *(pix+shift/8) = color>>shift;
1617 shift = surface->format->Bshift;
1618 *(pix+shift/8) = color>>shift;
1621 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1623 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1626 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1628 switch (dest->format->BytesPerPixel)
1631 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1635 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1639 _PutPixel24(dest,x,y,color);
1643 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1649 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1651 if (SDL_MUSTLOCK(surface))
1653 if (SDL_LockSurface(surface) < 0)
1659 _PutPixel(surface, x, y, color);
1661 if (SDL_MUSTLOCK(surface))
1663 SDL_UnlockSurface(surface);
1668 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1669 Uint8 r, Uint8 g, Uint8 b)
1671 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1674 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1676 if (y >= 0 && y <= dest->h - 1)
1678 switch (dest->format->BytesPerPixel)
1681 return y*dest->pitch;
1685 return y*dest->pitch/2;
1689 return y*dest->pitch;
1693 return y*dest->pitch/4;
1701 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1704 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1706 switch (surface->format->BytesPerPixel)
1711 *((Uint8 *)surface->pixels + ypitch + x) = color;
1717 // Probably 15-bpp or 16-bpp
1718 *((Uint16 *)surface->pixels + ypitch + x) = color;
1724 // Slow 24-bpp mode, usually not used
1728 // Gack - slow, but endian correct
1729 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1730 shift = surface->format->Rshift;
1731 *(pix+shift/8) = color>>shift;
1732 shift = surface->format->Gshift;
1733 *(pix+shift/8) = color>>shift;
1734 shift = surface->format->Bshift;
1735 *(pix+shift/8) = color>>shift;
1742 *((Uint32 *)surface->pixels + ypitch + x) = color;
1749 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1754 if (SDL_MUSTLOCK(Surface))
1756 if (SDL_LockSurface(Surface) < 0)
1770 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1774 if (x2 > Surface->w - 1)
1775 x2 = Surface->w - 1;
1782 SDL_FillRect(Surface, &l, Color);
1784 if (SDL_MUSTLOCK(Surface))
1786 SDL_UnlockSurface(Surface);
1790 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1791 Uint8 R, Uint8 G, Uint8 B)
1793 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1796 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1809 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1813 if (x2 > Surface->w - 1)
1814 x2 = Surface->w - 1;
1821 SDL_FillRect(Surface, &l, Color);
1824 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1829 if (SDL_MUSTLOCK(Surface))
1831 if (SDL_LockSurface(Surface) < 0)
1845 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1849 if (y2 > Surface->h - 1)
1850 y2 = Surface->h - 1;
1857 SDL_FillRect(Surface, &l, Color);
1859 if (SDL_MUSTLOCK(Surface))
1861 SDL_UnlockSurface(Surface);
1865 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1866 Uint8 R, Uint8 G, Uint8 B)
1868 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1871 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1884 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1888 if (y2 > Surface->h - 1)
1889 y2 = Surface->h - 1;
1896 SDL_FillRect(Surface, &l, Color);
1900 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1901 Sint16 x2, Sint16 y2, Uint32 Color,
1902 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1905 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1910 sdx = (dx < 0) ? -1 : 1;
1911 sdy = (dy < 0) ? -1 : 1;
1923 for (x = 0; x < dx; x++)
1925 Callback(Surface, px, py, Color);
1939 for (y = 0; y < dy; y++)
1941 Callback(Surface, px, py, Color);
1956 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1957 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1958 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1961 sge_DoLine(Surface, X1, Y1, X2, Y2,
1962 SDL_MapRGB(Surface->format, R, G, B), Callback);
1966 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1969 if (SDL_MUSTLOCK(Surface))
1971 if (SDL_LockSurface(Surface) < 0)
1976 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1978 // unlock the display
1979 if (SDL_MUSTLOCK(Surface))
1981 SDL_UnlockSurface(Surface);
1986 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1987 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1989 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1993 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1995 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1999 // ----------------------------------------------------------------------------
2000 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
2001 // ----------------------------------------------------------------------------
2003 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
2004 int width, int height, Uint32 color)
2008 for (y = src_y; y < src_y + height; y++)
2010 for (x = src_x; x < src_x + width; x++)
2012 Uint32 pixel = SDLGetPixel(bitmap, x, y);
2014 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
2019 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
2020 int src_x, int src_y, int width, int height,
2021 int dst_x, int dst_y)
2025 for (y = 0; y < height; y++)
2027 for (x = 0; x < width; x++)
2029 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
2031 if (pixel != BLACK_PIXEL)
2032 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
2038 // ============================================================================
2039 // The following functions were taken from the SDL_gfx library version 2.0.3
2040 // (Rotozoomer) by Andreas Schiffler
2041 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
2042 // ============================================================================
2044 // ----------------------------------------------------------------------------
2047 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
2048 // ----------------------------------------------------------------------------
2058 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
2061 tColorRGBA *sp, *csp, *dp;
2065 sp = csp = (tColorRGBA *) src->pixels;
2066 dp = (tColorRGBA *) dst->pixels;
2067 dgap = dst->pitch - dst->w * 4;
2069 for (y = 0; y < dst->h; y++)
2073 for (x = 0; x < dst->w; x++)
2075 tColorRGBA *sp0 = sp;
2076 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
2077 tColorRGBA *sp00 = &sp0[0];
2078 tColorRGBA *sp01 = &sp0[1];
2079 tColorRGBA *sp10 = &sp1[0];
2080 tColorRGBA *sp11 = &sp1[1];
2083 // create new color pixel from all four source color pixels
2084 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
2085 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
2086 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
2087 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
2092 // advance source pointers
2095 // advance destination pointer
2099 // advance source pointer
2100 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2102 // advance destination pointers
2103 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2109 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2111 int x, y, *sax, *say, *csax, *csay;
2113 tColorRGBA *sp, *csp, *csp0, *dp;
2116 // use specialized zoom function when scaling down to exactly half size
2117 if (src->w == 2 * dst->w &&
2118 src->h == 2 * dst->h)
2119 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2122 sx = (float) src->w / (float) dst->w;
2123 sy = (float) src->h / (float) dst->h;
2125 // allocate memory for row increments
2126 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2127 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2129 // precalculate row increments
2130 for (x = 0; x <= dst->w; x++)
2131 *csax++ = (int)(sx * x);
2133 for (y = 0; y <= dst->h; y++)
2134 *csay++ = (int)(sy * y);
2137 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2138 dp = (tColorRGBA *) dst->pixels;
2139 dgap = dst->pitch - dst->w * 4;
2142 for (y = 0; y < dst->h; y++)
2147 for (x = 0; x < dst->w; x++)
2152 // advance source pointers
2156 // advance destination pointer
2160 // advance source pointer
2162 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2164 // advance destination pointers
2165 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2174 // ----------------------------------------------------------------------------
2177 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2178 // ----------------------------------------------------------------------------
2180 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2182 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2183 Uint8 *sp, *dp, *csp;
2187 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2188 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2190 // allocate memory for row increments
2191 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2192 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2194 // precalculate row increments
2197 for (x = 0; x < dst->w; x++)
2200 *csax = (csx >> 16);
2207 for (y = 0; y < dst->h; y++)
2210 *csay = (csy >> 16);
2217 for (x = 0; x < dst->w; x++)
2225 for (y = 0; y < dst->h; y++)
2232 sp = csp = (Uint8 *) src->pixels;
2233 dp = (Uint8 *) dst->pixels;
2234 dgap = dst->pitch - dst->w;
2238 for (y = 0; y < dst->h; y++)
2242 for (x = 0; x < dst->w; x++)
2247 // advance source pointers
2251 // advance destination pointer
2255 // advance source pointer (for row)
2256 csp += ((*csay) * src->pitch);
2259 // advance destination pointers
2269 // ----------------------------------------------------------------------------
2272 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2273 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2274 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2275 // into a 32bit RGBA format on the fly.
2276 // ----------------------------------------------------------------------------
2278 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2280 SDL_Surface *zoom_src = NULL;
2281 SDL_Surface *zoom_dst = NULL;
2282 boolean is_converted = FALSE;
2289 // determine if source surface is 32 bit or 8 bit
2290 is_32bit = (src->format->BitsPerPixel == 32);
2292 if (is_32bit || src->format->BitsPerPixel == 8)
2294 // use source surface 'as is'
2299 // new source surface is 32 bit with a defined RGB ordering
2300 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2301 0x000000ff, 0x0000ff00, 0x00ff0000,
2302 (src->format->Amask ? 0xff000000 : 0));
2303 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2305 is_converted = TRUE;
2308 // allocate surface to completely contain the zoomed surface
2311 // target surface is 32 bit with source RGBA/ABGR ordering
2312 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2313 zoom_src->format->Rmask,
2314 zoom_src->format->Gmask,
2315 zoom_src->format->Bmask,
2316 zoom_src->format->Amask);
2320 // target surface is 8 bit
2321 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2325 // lock source surface
2326 SDL_LockSurface(zoom_src);
2328 // check which kind of surface we have
2331 // call the 32 bit transformation routine to do the zooming
2332 zoomSurfaceRGBA(zoom_src, zoom_dst);
2337 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2338 zoom_dst->format->palette->colors[i] =
2339 zoom_src->format->palette->colors[i];
2340 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2342 // call the 8 bit transformation routine to do the zooming
2343 zoomSurfaceY(zoom_src, zoom_dst);
2346 // unlock source surface
2347 SDL_UnlockSurface(zoom_src);
2349 // free temporary surface
2351 SDL_FreeSurface(zoom_src);
2353 // return destination surface
2357 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2359 SDL_Surface *new_surface;
2361 if (surface == NULL)
2364 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2365 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2367 // remove alpha channel from native non-transparent surface, if defined
2368 SDLSetAlpha(new_surface, FALSE, 0);
2370 // remove transparent color from native non-transparent surface, if defined
2371 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2376 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2378 Bitmap *dst_bitmap = CreateBitmapStruct();
2379 SDL_Surface *src_surface = src_bitmap->surface_masked;
2380 SDL_Surface *dst_surface;
2382 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2383 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2385 dst_bitmap->width = dst_width;
2386 dst_bitmap->height = dst_height;
2388 // create zoomed temporary surface from source surface
2389 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2391 // create native format destination surface from zoomed temporary surface
2392 SDLSetNativeSurface(&dst_surface);
2394 // set color key for zoomed surface from source surface, if defined
2395 if (SDLHasColorKey(src_surface))
2396 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2397 SDLGetColorKey(src_surface));
2399 // create native non-transparent surface for opaque blitting
2400 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2402 // set native transparent surface for masked blitting
2403 dst_bitmap->surface_masked = dst_surface;
2409 // ============================================================================
2410 // load image to bitmap
2411 // ============================================================================
2413 Bitmap *SDLLoadImage(char *filename)
2415 Bitmap *new_bitmap = CreateBitmapStruct();
2416 SDL_Surface *sdl_image_tmp;
2418 if (program.headless)
2420 // prevent sanity check warnings at later stage
2421 new_bitmap->width = new_bitmap->height = 1;
2426 print_timestamp_init("SDLLoadImage");
2428 print_timestamp_time(getBaseNamePtr(filename));
2430 // load image to temporary surface
2431 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2432 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
2434 print_timestamp_time("IMG_Load");
2436 UPDATE_BUSY_STATE();
2438 // create native non-transparent surface for current image
2439 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2440 Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2442 print_timestamp_time("SDLGetNativeSurface (opaque)");
2444 UPDATE_BUSY_STATE();
2446 // set black pixel to transparent if no alpha channel / transparent color
2447 if (!SDLHasAlpha(sdl_image_tmp) &&
2448 !SDLHasColorKey(sdl_image_tmp))
2449 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2450 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2452 // create native transparent surface for current image
2453 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2454 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2456 print_timestamp_time("SDLGetNativeSurface (masked)");
2458 UPDATE_BUSY_STATE();
2460 // free temporary surface
2461 SDL_FreeSurface(sdl_image_tmp);
2463 new_bitmap->width = new_bitmap->surface->w;
2464 new_bitmap->height = new_bitmap->surface->h;
2466 print_timestamp_done("SDLLoadImage");
2472 // ----------------------------------------------------------------------------
2473 // custom cursor fuctions
2474 // ----------------------------------------------------------------------------
2476 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2478 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2479 cursor_info->width, cursor_info->height,
2480 cursor_info->hot_x, cursor_info->hot_y);
2483 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2485 static struct MouseCursorInfo *last_cursor_info = NULL;
2486 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2487 static SDL_Cursor *cursor_default = NULL;
2488 static SDL_Cursor *cursor_current = NULL;
2490 // if invoked for the first time, store the SDL default cursor
2491 if (cursor_default == NULL)
2492 cursor_default = SDL_GetCursor();
2494 // only create new cursor if cursor info (custom only) has changed
2495 if (cursor_info != NULL && cursor_info != last_cursor_info)
2497 cursor_current = create_cursor(cursor_info);
2498 last_cursor_info = cursor_info;
2501 // only set new cursor if cursor info (custom or NULL) has changed
2502 if (cursor_info != last_cursor_info2)
2503 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2505 last_cursor_info2 = cursor_info;
2509 // ============================================================================
2511 // ============================================================================
2513 void SDLOpenAudio(void)
2515 if (program.headless)
2518 #if !defined(TARGET_SDL2)
2519 if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
2520 SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
2523 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2525 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2529 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2530 AUDIO_NUM_CHANNELS_STEREO,
2531 setup.system.audio_fragment_size) < 0)
2533 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2537 audio.sound_available = TRUE;
2538 audio.music_available = TRUE;
2539 audio.loops_available = TRUE;
2540 audio.sound_enabled = TRUE;
2542 // set number of available mixer channels
2543 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2544 audio.music_channel = MUSIC_CHANNEL;
2545 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2547 Mixer_InitChannels();
2550 void SDLCloseAudio(void)
2553 Mix_HaltChannel(-1);
2556 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2560 // ============================================================================
2562 // ============================================================================
2564 void SDLWaitEvent(Event *event)
2566 SDL_WaitEvent(event);
2569 void SDLHandleWindowManagerEvent(Event *event)
2572 #if defined(PLATFORM_WIN32)
2573 // experimental drag and drop code
2575 SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
2576 SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
2578 #if defined(TARGET_SDL2)
2579 if (syswmmsg->msg.win.msg == WM_DROPFILES)
2581 if (syswmmsg->msg == WM_DROPFILES)
2584 #if defined(TARGET_SDL2)
2585 HDROP hdrop = (HDROP)syswmmsg->msg.win.wParam;
2587 HDROP hdrop = (HDROP)syswmmsg->wParam;
2591 printf("::: SDL_SYSWMEVENT:\n");
2593 num_files = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
2595 for (i = 0; i < num_files; i++)
2597 int buffer_len = DragQueryFile(hdrop, i, NULL, 0);
2598 char buffer[buffer_len + 1];
2600 DragQueryFile(hdrop, i, buffer, buffer_len + 1);
2602 printf("::: - '%s'\n", buffer);
2605 #if defined(TARGET_SDL2)
2606 DragFinish((HDROP)syswmmsg->msg.win.wParam);
2608 DragFinish((HDROP)syswmmsg->wParam);
2616 // ============================================================================
2617 // joystick functions
2618 // ============================================================================
2620 #if defined(TARGET_SDL2)
2621 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2623 static SDL_Joystick *sdl_joystick[MAX_PLAYERS]; // only joysticks supported
2625 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2626 static int sdl_js_axis[MAX_PLAYERS][2];
2627 static int sdl_js_button[MAX_PLAYERS][2];
2628 static boolean sdl_is_controller[MAX_PLAYERS];
2630 void SDLClearJoystickState(void)
2634 for (i = 0; i < MAX_PLAYERS; i++)
2636 for (j = 0; j < 2; j++)
2638 sdl_js_axis_raw[i][j] = -1;
2639 sdl_js_axis[i][j] = 0;
2640 sdl_js_button[i][j] = 0;
2645 boolean SDLOpenJoystick(int nr)
2647 if (nr < 0 || nr >= MAX_PLAYERS)
2650 #if defined(TARGET_SDL2)
2651 sdl_is_controller[nr] = SDL_IsGameController(nr);
2653 sdl_is_controller[nr] = FALSE;
2657 Error(ERR_DEBUG, "opening joystick %d (%s)",
2658 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2661 #if defined(TARGET_SDL2)
2662 if (sdl_is_controller[nr])
2663 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2665 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2667 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2670 return (sdl_joystick[nr] != NULL);
2673 void SDLCloseJoystick(int nr)
2675 if (nr < 0 || nr >= MAX_PLAYERS)
2679 Error(ERR_DEBUG, "closing joystick %d", nr);
2682 #if defined(TARGET_SDL2)
2683 if (sdl_is_controller[nr])
2684 SDL_GameControllerClose(sdl_joystick[nr]);
2686 SDL_JoystickClose(sdl_joystick[nr]);
2688 SDL_JoystickClose(sdl_joystick[nr]);
2691 sdl_joystick[nr] = NULL;
2694 boolean SDLCheckJoystickOpened(int nr)
2696 if (nr < 0 || nr >= MAX_PLAYERS)
2699 #if defined(TARGET_SDL2)
2700 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2702 return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
2706 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2708 #if defined(TARGET_SDL2)
2709 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2710 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2711 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2712 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2714 int axis_id = axis_id_raw % 2;
2717 if (nr < 0 || nr >= MAX_PLAYERS)
2723 // prevent (slightly jittering, but centered) axis A from resetting axis B
2724 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2725 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2728 sdl_js_axis[nr][axis_id] = axis_value;
2729 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2732 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2734 #if defined(TARGET_SDL2)
2735 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2736 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2737 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2738 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2739 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2740 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2741 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2742 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2745 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2746 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2747 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2748 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2749 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2750 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2751 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2752 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2754 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2755 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2756 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2757 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2758 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2760 int button_id = button_id_raw % 2;
2763 if (nr < 0 || nr >= MAX_PLAYERS)
2766 if (button_id == -1)
2769 sdl_js_button[nr][button_id] = button_state;
2772 void HandleJoystickEvent(Event *event)
2776 #if defined(TARGET_SDL2)
2777 case SDL_CONTROLLERDEVICEADDED:
2779 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2780 event->cdevice.which);
2785 case SDL_CONTROLLERDEVICEREMOVED:
2787 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2788 event->cdevice.which);
2793 case SDL_CONTROLLERAXISMOTION:
2795 Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2796 event->caxis.which, event->caxis.axis, event->caxis.value);
2798 setJoystickAxis(event->caxis.which,
2800 event->caxis.value);
2803 case SDL_CONTROLLERBUTTONDOWN:
2805 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2806 event->cbutton.which, event->cbutton.button);
2808 setJoystickButton(event->cbutton.which,
2809 event->cbutton.button,
2813 case SDL_CONTROLLERBUTTONUP:
2815 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2816 event->cbutton.which, event->cbutton.button);
2818 setJoystickButton(event->cbutton.which,
2819 event->cbutton.button,
2824 case SDL_JOYAXISMOTION:
2825 if (sdl_is_controller[event->jaxis.which])
2829 Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2830 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2832 if (event->jaxis.axis < 4)
2833 setJoystickAxis(event->jaxis.which,
2835 event->jaxis.value);
2838 case SDL_JOYBUTTONDOWN:
2839 if (sdl_is_controller[event->jaxis.which])
2843 Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2844 event->jbutton.which, event->jbutton.button);
2846 if (event->jbutton.button < 4)
2847 setJoystickButton(event->jbutton.which,
2848 event->jbutton.button,
2852 case SDL_JOYBUTTONUP:
2853 if (sdl_is_controller[event->jaxis.which])
2857 Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2858 event->jbutton.which, event->jbutton.button);
2860 if (event->jbutton.button < 4)
2861 setJoystickButton(event->jbutton.which,
2862 event->jbutton.button,
2871 void SDLInitJoysticks(void)
2873 static boolean sdl_joystick_subsystem_initialized = FALSE;
2874 boolean print_warning = !sdl_joystick_subsystem_initialized;
2875 #if defined(TARGET_SDL2)
2876 char *mappings_file_base = getPath2(options.conf_directory,
2877 GAMECONTROLLER_BASENAME);
2878 char *mappings_file_user = getPath2(getUserGameDataDir(),
2879 GAMECONTROLLER_BASENAME);
2884 if (!sdl_joystick_subsystem_initialized)
2886 sdl_joystick_subsystem_initialized = TRUE;
2888 #if defined(TARGET_SDL2)
2889 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2891 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2893 if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
2896 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2900 #if defined(TARGET_SDL2)
2901 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2903 // the included game controller base mappings should always be found
2904 if (num_mappings == -1)
2905 Error(ERR_WARN, "no game controller base mappings found");
2908 Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2911 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2914 // the personal game controller user mappings may or may not be found
2915 if (num_mappings == -1)
2916 Error(ERR_WARN, "no game controller user mappings found");
2918 Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2920 Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2923 checked_free(mappings_file_base);
2924 checked_free(mappings_file_user);
2927 for (i = 0; i < SDL_NumJoysticks(); i++)
2929 const char *name, *type;
2931 if (SDL_IsGameController(i))
2933 name = SDL_GameControllerNameForIndex(i);
2934 type = "game controller";
2938 name = SDL_JoystickNameForIndex(i);
2942 Error(ERR_INFO, "- joystick %d (%s): '%s'",
2943 i, type, (name ? name : "(Unknown)"));
2949 // assign joysticks from configured to connected joystick for all players
2950 for (i = 0; i < MAX_PLAYERS; i++)
2952 // get configured joystick for this player
2953 char *device_name = setup.input[i].joy.device_name;
2954 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2956 if (joystick_nr >= SDL_NumJoysticks())
2958 if (setup.input[i].use_joystick && print_warning)
2959 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2964 // store configured joystick number for each player
2965 joystick.nr[i] = joystick_nr;
2968 // now open all connected joysticks (regardless if configured or not)
2969 for (i = 0; i < SDL_NumJoysticks(); i++)
2971 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2972 if (SDLCheckJoystickOpened(i))
2973 SDLCloseJoystick(i);
2975 if (SDLOpenJoystick(i))
2976 joystick.status = JOYSTICK_ACTIVATED;
2977 else if (print_warning)
2978 Error(ERR_WARN, "cannot open joystick %d", i);
2981 SDLClearJoystickState();
2984 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2986 if (nr < 0 || nr >= MAX_PLAYERS)
2990 *x = sdl_js_axis[nr][0];
2992 *y = sdl_js_axis[nr][1];
2995 *b1 = sdl_js_button[nr][0];
2997 *b2 = sdl_js_button[nr][1];
3003 // ============================================================================
3004 // touch input overlay functions
3005 // ============================================================================
3007 #if defined(USE_TOUCH_INPUT_OVERLAY)
3008 static void DrawTouchInputOverlay_ShowGrid(int alpha)
3011 int grid_xsize = overlay.grid_xsize;
3012 int grid_ysize = overlay.grid_ysize;
3015 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
3016 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
3018 for (x = 0; x < grid_xsize; x++)
3020 rect.x = (x + 0) * video.screen_width / grid_xsize;
3021 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
3023 for (y = 0; y < grid_ysize; y++)
3025 rect.y = (y + 0) * video.screen_height / grid_ysize;
3026 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
3028 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
3029 SDL_RenderDrawRect(sdl_renderer, &rect);
3033 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3036 static void RenderFillRectangle(int x, int y, int width, int height)
3038 SDL_Rect rect = { x, y, width, height };
3040 SDL_RenderFillRect(sdl_renderer, &rect);
3043 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
3045 static int alpha_direction = 0;
3046 static int alpha_highlight = 0;
3047 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3048 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3050 int grid_xsize = overlay.grid_xsize;
3051 int grid_ysize = overlay.grid_ysize;
3054 if (alpha == alpha_max)
3056 if (alpha_direction < 0)
3058 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
3060 if (alpha_highlight == 0)
3061 alpha_direction = 1;
3065 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
3067 if (alpha_highlight == alpha_max)
3068 alpha_direction = -1;
3073 alpha_direction = 1;
3074 alpha_highlight = alpha;
3077 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
3079 for (x = 0; x < grid_xsize; x++)
3081 for (y = 0; y < grid_ysize; y++)
3083 int grid_button = overlay.grid_button[x][y];
3084 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
3085 int alpha_draw = alpha;
3086 int outline_border = MV_NONE;
3087 int border_size = 2;
3088 boolean draw_outlined = setup.touch.draw_outlined;
3089 boolean draw_pressed = setup.touch.draw_pressed;
3091 if (grid_button == CHAR_GRID_BUTTON_NONE)
3094 if (grid_button == overlay.grid_button_highlight)
3095 alpha_draw = alpha_highlight;
3097 if (draw_pressed && overlay.grid_button_action & grid_button_action)
3100 draw_outlined = FALSE;
3102 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
3105 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
3107 rect.x = (x + 0) * video.screen_width / grid_xsize;
3108 rect.y = (y + 0) * video.screen_height / grid_ysize;
3109 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
3110 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
3112 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
3114 rect.x += border_size;
3115 rect.w -= border_size;
3117 outline_border |= MV_LEFT;
3120 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
3122 rect.w -= border_size;
3124 outline_border |= MV_RIGHT;
3127 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
3129 rect.y += border_size;
3130 rect.h -= border_size;
3132 outline_border |= MV_UP;
3135 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
3137 rect.h -= border_size;
3139 outline_border |= MV_DOWN;
3144 int rect_x = rect.x +
3145 (outline_border & MV_LEFT ? border_size : 0);
3146 int rect_w = rect.w -
3147 (outline_border & MV_LEFT ? border_size : 0) -
3148 (outline_border & MV_RIGHT ? border_size : 0);
3150 if (outline_border & MV_LEFT)
3151 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
3153 if (outline_border & MV_RIGHT)
3154 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
3155 border_size, rect.h);
3157 if (outline_border & MV_UP)
3158 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
3160 if (outline_border & MV_DOWN)
3161 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
3162 rect_w, border_size);
3166 SDL_RenderFillRect(sdl_renderer, &rect);
3171 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3174 static void DrawTouchInputOverlay(void)
3176 static SDL_Texture *texture = NULL;
3177 static boolean initialized = FALSE;
3178 static boolean deactivated = TRUE;
3179 static boolean show_grid = FALSE;
3180 static int width = 0, height = 0;
3181 static int alpha_last = -1;
3182 static int alpha = 0;
3183 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3184 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3185 boolean active = (overlay.enabled && overlay.active);
3187 if (!active && deactivated)
3192 if (alpha < alpha_max)
3193 alpha = MIN(alpha + alpha_step, alpha_max);
3195 deactivated = FALSE;
3199 alpha = MAX(0, alpha - alpha_step);
3205 if (overlay.show_grid)
3207 else if (deactivated)
3211 DrawTouchInputOverlay_ShowGrid(alpha);
3213 DrawTouchInputOverlay_ShowGridButtons(alpha);
3218 // !!! VIRTUAL BUTTONS FROM IMAGE FILE NOT USED ANYMORE !!!
3222 char *basename = "overlay/VirtualButtons.png";
3223 char *filename = getCustomImageFilename(basename);
3225 if (filename == NULL)
3226 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
3228 SDL_Surface *surface;
3230 if ((surface = IMG_Load(filename)) == NULL)
3231 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
3234 height = surface->h;
3236 // set black pixel to transparent if no alpha channel / transparent color
3237 if (!SDLHasAlpha(surface) &&
3238 !SDLHasColorKey(surface))
3239 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
3240 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
3242 if ((texture = SDLCreateTextureFromSurface(surface)) == NULL)
3243 Error(ERR_EXIT, "SDLCreateTextureFromSurface() failed");
3245 SDL_FreeSurface(surface);
3247 SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
3252 if (alpha != alpha_last)
3253 SDL_SetTextureAlphaMod(texture, alpha);
3257 float ratio_overlay = (float) width / height;
3258 float ratio_screen = (float) video.screen_width / video.screen_height;
3259 int width_scaled, height_scaled;
3262 if (ratio_overlay > ratio_screen)
3264 width_scaled = video.screen_width;
3265 height_scaled = video.screen_height * ratio_screen / ratio_overlay;
3267 ypos = video.screen_height - height_scaled;
3271 width_scaled = video.screen_width * ratio_overlay / ratio_screen;
3272 height_scaled = video.screen_height;
3273 xpos = (video.screen_width - width_scaled) / 2;
3277 SDL_Rect src_rect = { 0, 0, width, height };
3278 SDL_Rect dst_rect = { xpos, ypos, width_scaled, height_scaled };
3280 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);