1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
19 #define ENABLE_UNUSED_CODE 0 // currently unused functions
21 #define DEBUG_JOYSTICKS 0
24 // ============================================================================
26 // ============================================================================
28 // SDL internal variables
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;
34 static boolean limit_screen_updates = FALSE;
37 // functions from SGE library
38 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
40 #if defined(USE_TOUCH_INPUT_OVERLAY)
41 // functions to draw overlay graphics for touch device input
42 static void DrawTouchInputOverlay(void);
43 static void DrawTouchGadgetsOverlay(void);
46 void SDLLimitScreenUpdates(boolean enable)
48 limit_screen_updates = enable;
51 static void FinalizeScreen(int draw_target)
53 // copy global animations to render target buffer, if defined (below border)
54 if (gfx.draw_global_anim_function != NULL)
55 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
57 // copy global masked border to render target buffer, if defined
58 if (gfx.draw_global_border_function != NULL)
59 gfx.draw_global_border_function(draw_target);
61 // copy global animations to render target buffer, if defined (above border)
62 if (gfx.draw_global_anim_function != NULL)
63 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
65 // copy tile selection cursor to render target buffer, if defined (above all)
66 if (gfx.draw_tile_cursor_function != NULL)
67 gfx.draw_tile_cursor_function(draw_target);
70 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
72 static unsigned int update_screen_delay = 0;
73 unsigned int update_screen_delay_value = 50; // (milliseconds)
74 SDL_Surface *screen = backbuffer->surface;
76 if (limit_screen_updates &&
77 !DelayReached(&update_screen_delay, update_screen_delay_value))
80 LimitScreenUpdates(FALSE);
84 static int LastFrameCounter = 0;
85 boolean changed = (FrameCounter != LastFrameCounter);
87 printf("::: FrameCounter == %d [%s]\n", FrameCounter,
88 (changed ? "-" : "SAME FRAME UPDATED"));
90 LastFrameCounter = FrameCounter;
99 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
100 gfx.final_screen_bitmap != NULL) // may not be initialized yet
102 // draw global animations using bitmaps instead of using textures
103 // to prevent texture scaling artefacts (this is potentially slower)
105 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
106 gfx.win_xsize, gfx.win_ysize, 0, 0);
108 FinalizeScreen(DRAW_TO_SCREEN);
110 screen = gfx.final_screen_bitmap->surface;
112 // force full window redraw
116 SDL_Texture *sdl_texture = sdl_texture_stream;
118 // deactivate use of target texture if render targets are not supported
119 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
120 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
121 sdl_texture_target == NULL)
122 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
124 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
125 sdl_texture = sdl_texture_target;
129 int bytes_x = screen->pitch / video.width;
130 int bytes_y = screen->pitch;
132 SDL_UpdateTexture(sdl_texture, rect,
133 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
138 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
141 int xoff = video.screen_xoffset;
142 int yoff = video.screen_yoffset;
143 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
144 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
145 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
147 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
148 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
149 dst_rect2 = &dst_rect_screen;
151 dst_rect1 = &dst_rect_screen;
153 #if defined(HAS_SCREEN_KEYBOARD)
154 if (video.shifted_up || video.shifted_up_delay)
156 int time_current = SDL_GetTicks();
157 int pos = video.shifted_up_pos;
158 int pos_last = video.shifted_up_pos_last;
160 if (!DelayReachedExt(&video.shifted_up_delay, video.shifted_up_delay_value,
163 int delay = time_current - video.shifted_up_delay;
164 int delay_value = video.shifted_up_delay_value;
166 pos = pos_last + (pos - pos_last) * delay / delay_value;
170 video.shifted_up_pos_last = pos;
171 video.shifted_up_delay = 0;
174 SDL_Rect src_rect_up = { 0, pos, video.width, video.height - pos };
175 SDL_Rect dst_rect_up = { xoff, yoff, video.width, video.height - pos };
177 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
178 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
180 src_rect2 = &src_rect_up;
181 dst_rect2 = &dst_rect_up;
185 src_rect1 = &src_rect_up;
186 dst_rect1 = &dst_rect_up;
191 // clear render target buffer
192 SDL_RenderClear(sdl_renderer);
194 // set renderer to use target texture for rendering
195 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
196 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
197 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
199 // copy backbuffer texture to render target buffer
200 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
201 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
203 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
204 FinalizeScreen(DRAW_TO_SCREEN);
206 // when using target texture, copy it to screen buffer
207 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
208 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
210 SDL_SetRenderTarget(sdl_renderer, NULL);
211 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
214 #if defined(USE_TOUCH_INPUT_OVERLAY)
215 // draw overlay graphics for touch device input, if needed
216 DrawTouchInputOverlay();
218 // draw overlay gadgets for touch device input, if needed
219 DrawTouchGadgetsOverlay();
222 // global synchronization point of the game to align video frame delay
223 if (with_frame_delay)
224 WaitUntilDelayReached(&video.frame_delay, video.frame_delay_value);
226 video.frame_counter++;
228 // show render target buffer on screen
229 SDL_RenderPresent(sdl_renderer);
232 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
234 PumpEvents(); // execute event filter actions while waiting
236 UpdateScreenExt(rect, TRUE);
239 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
241 UpdateScreenExt(rect, FALSE);
244 void Delay_WithScreenUpdates(unsigned int delay)
246 unsigned int time_current = SDL_GetTicks();
247 unsigned int time_delayed = time_current + delay;
249 while (time_current < time_delayed)
251 // updating the screen contains waiting for frame delay (non-busy)
252 UpdateScreen_WithFrameDelay(NULL);
254 time_current = SDL_GetTicks();
258 static void SDLSetWindowIcon(char *basename)
260 // (setting the window icon on Mac OS X would replace the high-quality
261 // dock icon with the currently smaller (and uglier) icon from file)
263 #if !defined(PLATFORM_MACOSX)
264 char *filename = getCustomImageFilename(basename);
265 SDL_Surface *surface;
267 if (filename == NULL)
269 Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
274 if ((surface = IMG_Load(filename)) == NULL)
276 Error(ERR_WARN, "IMG_Load('%s') failed: %s", basename, SDL_GetError());
281 // set transparent color
282 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
283 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
285 SDL_SetWindowIcon(sdl_window, surface);
289 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
290 SDL_PixelFormat *format2)
292 return (format1->format == format2->format &&
293 format1->BitsPerPixel == format2->BitsPerPixel &&
294 format1->BytesPerPixel == format2->BytesPerPixel &&
295 format1->Rmask == format2->Rmask &&
296 format1->Gmask == format2->Gmask &&
297 format1->Bmask == format2->Bmask);
300 static Pixel SDLGetColorKey(SDL_Surface *surface)
304 if (SDL_GetColorKey(surface, &color_key) != 0)
310 static boolean SDLHasColorKey(SDL_Surface *surface)
312 return (SDLGetColorKey(surface) != -1);
315 static boolean SDLHasAlpha(SDL_Surface *surface)
317 SDL_BlendMode blend_mode;
319 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
322 return (blend_mode == SDL_BLENDMODE_BLEND);
325 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
327 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
329 SDL_SetSurfaceBlendMode(surface, blend_mode);
330 SDL_SetSurfaceAlphaMod(surface, alpha);
333 const char *SDLGetRendererName(void)
335 static SDL_RendererInfo renderer_info;
337 SDL_GetRendererInfo(sdl_renderer, &renderer_info);
339 return renderer_info.name;
342 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
344 SDL_PixelFormat format;
345 SDL_Surface *new_surface;
350 if (backbuffer && backbuffer->surface)
352 format = *backbuffer->surface->format;
353 format.Amask = surface->format->Amask; // keep alpha channel
357 format = *surface->format;
360 new_surface = SDL_ConvertSurface(surface, &format, 0);
362 if (new_surface == NULL)
363 Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
365 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
366 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
367 SDL_SetColorKey(new_surface, SET_TRANSPARENT_PIXEL,
368 SDLGetColorKey(surface));
373 boolean SDLSetNativeSurface(SDL_Surface **surface)
375 SDL_Surface *new_surface;
377 if (surface == NULL ||
379 backbuffer == NULL ||
380 backbuffer->surface == NULL)
383 // if pixel format already optimized for destination surface, do nothing
384 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
387 new_surface = SDLGetNativeSurface(*surface);
389 SDL_FreeSurface(*surface);
391 *surface = new_surface;
396 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
398 if (program.headless)
401 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
404 Error(ERR_EXIT, "SDL_CreateTextureFromSurface() failed: %s",
410 void SDLCreateBitmapTextures(Bitmap *bitmap)
416 SDL_DestroyTexture(bitmap->texture);
417 if (bitmap->texture_masked)
418 SDL_DestroyTexture(bitmap->texture_masked);
420 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
421 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
424 void SDLFreeBitmapTextures(Bitmap *bitmap)
430 SDL_DestroyTexture(bitmap->texture);
431 if (bitmap->texture_masked)
432 SDL_DestroyTexture(bitmap->texture_masked);
434 bitmap->texture = NULL;
435 bitmap->texture_masked = NULL;
438 void SDLInitVideoDisplay(void)
440 // set hint to select render driver as specified in setup config file
441 if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
442 SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
444 // initialize SDL video
445 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
446 Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
448 // set default SDL depth
449 video.default_depth = 32; // (how to determine video depth in SDL2?)
451 // Code used with SDL 1.2:
452 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
455 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
457 if (program.headless)
460 video.window_scaling_percent = setup.window_scaling_percent;
461 video.window_scaling_quality = setup.window_scaling_quality;
463 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
465 // SDL 2.0: support for (desktop) fullscreen mode available
466 video.fullscreen_available = TRUE;
468 // open SDL video output device (window or fullscreen mode)
469 if (!SDLSetVideoMode(fullscreen))
470 Error(ERR_EXIT, "setting video mode failed");
472 // !!! SDL2 can only set the window icon if the window already exists !!!
474 SDLSetWindowIcon(program.icon_filename);
476 // set window and icon title
480 static void SDLInitVideoBuffer_DrawBuffer(void)
482 /* SDL cannot directly draw to the visible video framebuffer like X11,
483 but always uses a backbuffer, which is then blitted to the visible
484 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
485 visible video framebuffer with 'SDL_Flip', if the hardware supports
486 this). Therefore do not use an additional backbuffer for drawing, but
487 use a symbolic buffer (distinguishable from the SDL backbuffer) called
488 'window', which indicates that the SDL backbuffer should be updated to
489 the visible video framebuffer when attempting to blit to it.
491 For convenience, it seems to be a good idea to create this symbolic
492 buffer 'window' at the same size as the SDL backbuffer. Although it
493 should never be drawn to directly, it would do no harm nevertheless. */
495 // create additional (symbolic) buffer for double-buffering
496 ReCreateBitmap(&window, video.width, video.height);
498 // create dummy drawing buffer for headless mode, if needed
499 if (program.headless)
500 ReCreateBitmap(&backbuffer, video.width, video.height);
503 void SDLInitVideoBuffer(boolean fullscreen)
505 SDLInitVideoBuffer_VideoBuffer(fullscreen);
506 SDLInitVideoBuffer_DrawBuffer();
509 static boolean SDLCreateScreen(boolean fullscreen)
511 SDL_Surface *new_surface = NULL;
513 int surface_flags_window = SURFACE_FLAGS;
514 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
517 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
519 video.vsync_mode = VSYNC_MODE_OFF;
521 if (!strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF))
523 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
524 video.vsync_mode = VSYNC_MODE_NORMAL;
527 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
528 _without_ enabling 2D/3D acceleration and/or guest additions installed,
529 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
530 it will try to use accelerated graphics and apparently fails miserably) */
531 int renderer_flags = SDL_RENDERER_SOFTWARE;
534 int width = video.width;
535 int height = video.height;
536 int screen_width = video.screen_width;
537 int screen_height = video.screen_height;
538 int surface_flags = (fullscreen ? surface_flags_fullscreen :
539 surface_flags_window);
541 // default window size is unscaled
542 video.window_width = screen_width;
543 video.window_height = screen_height;
545 // store if initial screen mode is fullscreen mode when changing screen size
546 video.fullscreen_initial = fullscreen;
548 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
550 video.window_width = window_scaling_factor * screen_width;
551 video.window_height = window_scaling_factor * screen_height;
553 if (sdl_texture_stream)
555 SDL_DestroyTexture(sdl_texture_stream);
556 sdl_texture_stream = NULL;
559 if (sdl_texture_target)
561 SDL_DestroyTexture(sdl_texture_target);
562 sdl_texture_target = NULL;
565 if (!(fullscreen && fullscreen_enabled))
569 SDL_DestroyRenderer(sdl_renderer);
575 SDL_DestroyWindow(sdl_window);
580 if (sdl_window == NULL)
581 sdl_window = SDL_CreateWindow(program.window_title,
582 SDL_WINDOWPOS_CENTERED,
583 SDL_WINDOWPOS_CENTERED,
588 if (sdl_window != NULL)
590 if (sdl_renderer == NULL)
591 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
593 if (sdl_renderer != NULL)
595 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
596 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
598 // required for setting adaptive vsync when using OpenGL renderer
599 SDLSetScreenVsyncMode(setup.vsync_mode);
601 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
602 SDL_PIXELFORMAT_ARGB8888,
603 SDL_TEXTUREACCESS_STREAMING,
606 if (SDL_RenderTargetSupported(sdl_renderer))
607 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
608 SDL_PIXELFORMAT_ARGB8888,
609 SDL_TEXTUREACCESS_TARGET,
612 if (sdl_texture_stream != NULL)
614 // use SDL default values for RGB masks and no alpha channel
615 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
617 if (new_surface == NULL)
618 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
622 Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
627 Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
632 Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
635 SDLSetScreenProperties();
637 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
638 if (new_surface != NULL)
639 fullscreen_enabled = fullscreen;
641 if (backbuffer == NULL)
642 backbuffer = CreateBitmapStruct();
644 backbuffer->width = video.width;
645 backbuffer->height = video.height;
647 if (backbuffer->surface)
648 SDL_FreeSurface(backbuffer->surface);
650 backbuffer->surface = new_surface;
652 return (new_surface != NULL);
655 boolean SDLSetVideoMode(boolean fullscreen)
657 boolean success = FALSE;
661 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
663 // switch display to fullscreen mode, if available
664 success = SDLCreateScreen(TRUE);
668 // switching display to fullscreen mode failed -- do not try it again
669 video.fullscreen_available = FALSE;
673 video.fullscreen_enabled = TRUE;
677 if ((!fullscreen && video.fullscreen_enabled) || !success)
679 // switch display to window mode
680 success = SDLCreateScreen(FALSE);
684 // switching display to window mode failed -- should not happen
688 video.fullscreen_enabled = FALSE;
689 video.window_scaling_percent = setup.window_scaling_percent;
690 video.window_scaling_quality = setup.window_scaling_quality;
692 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
696 SDLRedrawWindow(); // map window
701 void SDLSetWindowTitle(void)
703 if (sdl_window == NULL)
706 SDL_SetWindowTitle(sdl_window, program.window_title);
709 void SDLSetWindowScaling(int window_scaling_percent)
711 if (sdl_window == NULL)
714 float window_scaling_factor = (float)window_scaling_percent / 100;
715 int new_window_width = (int)(window_scaling_factor * video.screen_width);
716 int new_window_height = (int)(window_scaling_factor * video.screen_height);
718 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
720 video.window_scaling_percent = window_scaling_percent;
721 video.window_width = new_window_width;
722 video.window_height = new_window_height;
727 void SDLSetWindowScalingQuality(char *window_scaling_quality)
729 SDL_Texture *new_texture;
731 if (sdl_texture_stream == NULL)
734 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
736 new_texture = SDL_CreateTexture(sdl_renderer,
737 SDL_PIXELFORMAT_ARGB8888,
738 SDL_TEXTUREACCESS_STREAMING,
739 video.width, video.height);
741 if (new_texture != NULL)
743 SDL_DestroyTexture(sdl_texture_stream);
745 sdl_texture_stream = new_texture;
748 if (SDL_RenderTargetSupported(sdl_renderer))
749 new_texture = SDL_CreateTexture(sdl_renderer,
750 SDL_PIXELFORMAT_ARGB8888,
751 SDL_TEXTUREACCESS_TARGET,
752 video.width, video.height);
756 if (new_texture != NULL)
758 SDL_DestroyTexture(sdl_texture_target);
760 sdl_texture_target = new_texture;
765 video.window_scaling_quality = window_scaling_quality;
768 void SDLSetWindowFullscreen(boolean fullscreen)
770 if (sdl_window == NULL)
773 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
775 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
776 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
778 // if screen size was changed in fullscreen mode, correct desktop window size
779 if (!fullscreen && video.fullscreen_initial)
781 SDLSetWindowScaling(setup.window_scaling_percent);
782 SDL_SetWindowPosition(sdl_window,
783 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
785 video.fullscreen_initial = FALSE;
789 void SDLSetDisplaySize(void)
791 if (sdl_renderer != NULL)
795 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
797 video.display_width = w;
798 video.display_height = h;
801 Error(ERR_DEBUG, "SDL renderer size: %d x %d",
802 video.display_width, video.display_height);
807 SDL_Rect display_bounds;
809 SDL_GetDisplayBounds(0, &display_bounds);
811 video.display_width = display_bounds.w;
812 video.display_height = display_bounds.h;
815 Error(ERR_DEBUG, "SDL display size: %d x %d",
816 video.display_width, video.display_height);
821 void SDLSetScreenSizeAndOffsets(int width, int height)
823 // set default video screen size and offsets
824 video.screen_width = width;
825 video.screen_height = height;
826 video.screen_xoffset = 0;
827 video.screen_yoffset = 0;
829 #if defined(USE_COMPLETE_DISPLAY)
830 float ratio_video = (float) width / height;
831 float ratio_display = (float) video.display_width / video.display_height;
833 if (ratio_video != ratio_display)
835 // adjust drawable screen size to cover the whole device display
837 if (ratio_video < ratio_display)
838 video.screen_width *= ratio_display / ratio_video;
840 video.screen_height *= ratio_video / ratio_display;
842 video.screen_xoffset = (video.screen_width - width) / 2;
843 video.screen_yoffset = (video.screen_height - height) / 2;
846 Error(ERR_DEBUG, "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
848 video.screen_width, video.screen_height,
849 ratio_video, ratio_display);
855 void SDLSetScreenSizeForRenderer(int width, int height)
857 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
860 void SDLSetScreenProperties(void)
863 SDLSetScreenSizeAndOffsets(video.width, video.height);
864 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
867 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
869 video.screen_rendering_mode =
870 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
871 SPECIAL_RENDERING_BITMAP :
872 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
873 SPECIAL_RENDERING_TARGET:
874 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
875 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
878 void SDLSetScreenVsyncMode(char *vsync_mode)
880 // changing vsync mode without re-creating renderer only supported by OpenGL
881 if (!strPrefixLower((char *)SDLGetRendererName(), "opengl"))
884 int interval = VSYNC_MODE_STR_TO_INT(vsync_mode);
885 int result = SDL_GL_SetSwapInterval(interval);
887 // if adaptive vsync requested, but not supported, retry with normal vsync
888 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
890 interval = VSYNC_MODE_NORMAL;
892 result = SDL_GL_SetSwapInterval(interval);
896 interval = VSYNC_MODE_OFF;
898 video.vsync_mode = interval;
901 void SDLRedrawWindow(void)
903 UpdateScreen_WithoutFrameDelay(NULL);
906 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
909 if (program.headless)
912 SDL_Surface *surface =
913 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
916 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
918 SDLSetNativeSurface(&surface);
920 bitmap->surface = surface;
923 void SDLFreeBitmapPointers(Bitmap *bitmap)
926 SDL_FreeSurface(bitmap->surface);
927 if (bitmap->surface_masked)
928 SDL_FreeSurface(bitmap->surface_masked);
930 bitmap->surface = NULL;
931 bitmap->surface_masked = NULL;
934 SDL_DestroyTexture(bitmap->texture);
935 if (bitmap->texture_masked)
936 SDL_DestroyTexture(bitmap->texture_masked);
938 bitmap->texture = NULL;
939 bitmap->texture_masked = NULL;
942 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
943 int src_x, int src_y, int width, int height,
944 int dst_x, int dst_y, int mask_mode)
946 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
947 SDL_Rect src_rect, dst_rect;
959 // if (src_bitmap != backbuffer || dst_bitmap != window)
960 if (!(src_bitmap == backbuffer && dst_bitmap == window))
961 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
962 src_bitmap->surface_masked : src_bitmap->surface),
963 &src_rect, real_dst_bitmap->surface, &dst_rect);
965 if (dst_bitmap == window)
966 UpdateScreen_WithFrameDelay(&dst_rect);
969 void SDLBlitTexture(Bitmap *bitmap,
970 int src_x, int src_y, int width, int height,
971 int dst_x, int dst_y, int mask_mode)
973 SDL_Texture *texture;
978 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
993 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
996 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
999 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1007 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1009 if (dst_bitmap == window)
1010 UpdateScreen_WithFrameDelay(&rect);
1013 void PrepareFadeBitmap(int draw_target)
1015 Bitmap *fade_bitmap =
1016 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1017 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1019 if (fade_bitmap == NULL)
1022 // copy backbuffer to fading buffer
1023 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1025 // add border and animations to fading buffer
1026 FinalizeScreen(draw_target);
1029 void SDLFadeRectangle(int x, int y, int width, int height,
1030 int fade_mode, int fade_delay, int post_delay,
1031 void (*draw_border_function)(void))
1033 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1034 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1035 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1036 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1037 SDL_Surface *surface_screen = backbuffer->surface;
1038 SDL_Rect src_rect, dst_rect;
1040 int src_x = x, src_y = y;
1041 int dst_x = x, dst_y = y;
1042 unsigned int time_last, time_current;
1044 // store function for drawing global masked border
1045 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1047 // deactivate drawing of global border while fading, if needed
1048 if (draw_border_function == NULL)
1049 gfx.draw_global_border_function = NULL;
1054 src_rect.h = height;
1058 dst_rect.w = width; // (ignored)
1059 dst_rect.h = height; // (ignored)
1061 dst_rect2 = dst_rect;
1063 // before fading in, store backbuffer (without animation graphics)
1064 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1065 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1067 // copy source and target surfaces to temporary surfaces for fading
1068 if (fade_mode & FADE_TYPE_TRANSFORM)
1070 // (source and target fading buffer already prepared)
1072 else if (fade_mode & FADE_TYPE_FADE_IN)
1074 // (target fading buffer already prepared)
1075 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1077 else // FADE_TYPE_FADE_OUT
1079 // (source fading buffer already prepared)
1080 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1083 time_current = SDL_GetTicks();
1085 if (fade_delay <= 0)
1087 // immediately draw final target frame without delay
1088 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1092 // when fading without delay, also skip post delay
1096 if (fade_mode == FADE_MODE_MELT)
1098 boolean done = FALSE;
1099 int melt_pixels = 2;
1100 int melt_columns = width / melt_pixels;
1101 int ypos[melt_columns];
1102 int max_steps = height / 8 + 32;
1107 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1109 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1111 ypos[0] = -GetSimpleRandom(16);
1113 for (i = 1 ; i < melt_columns; i++)
1115 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1117 ypos[i] = ypos[i - 1] + r;
1130 time_last = time_current;
1131 time_current = SDL_GetTicks();
1132 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1133 steps_final = MIN(MAX(0, steps), max_steps);
1137 done = (steps_done >= steps_final);
1139 for (i = 0 ; i < melt_columns; i++)
1147 else if (ypos[i] < height)
1152 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1154 if (ypos[i] + dy >= height)
1155 dy = height - ypos[i];
1157 // copy part of (appearing) target surface to upper area
1158 src_rect.x = src_x + i * melt_pixels;
1159 // src_rect.y = src_y + ypos[i];
1161 src_rect.w = melt_pixels;
1163 src_rect.h = ypos[i] + dy;
1165 dst_rect.x = dst_x + i * melt_pixels;
1166 // dst_rect.y = dst_y + ypos[i];
1169 if (steps_done >= steps_final)
1170 SDL_BlitSurface(surface_target, &src_rect,
1171 surface_screen, &dst_rect);
1175 // copy part of (disappearing) source surface to lower area
1176 src_rect.x = src_x + i * melt_pixels;
1178 src_rect.w = melt_pixels;
1179 src_rect.h = height - ypos[i];
1181 dst_rect.x = dst_x + i * melt_pixels;
1182 dst_rect.y = dst_y + ypos[i];
1184 if (steps_done >= steps_final)
1185 SDL_BlitSurface(surface_source, &src_rect,
1186 surface_screen, &dst_rect);
1192 src_rect.x = src_x + i * melt_pixels;
1194 src_rect.w = melt_pixels;
1195 src_rect.h = height;
1197 dst_rect.x = dst_x + i * melt_pixels;
1200 if (steps_done >= steps_final)
1201 SDL_BlitSurface(surface_target, &src_rect,
1202 surface_screen, &dst_rect);
1206 if (steps_done >= steps_final)
1208 if (draw_border_function != NULL)
1209 draw_border_function();
1211 UpdateScreen_WithFrameDelay(&dst_rect2);
1215 else if (fade_mode == FADE_MODE_CURTAIN)
1219 int xx_size = width / 2;
1221 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1223 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1225 for (xx = 0; xx < xx_size;)
1227 time_last = time_current;
1228 time_current = SDL_GetTicks();
1229 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1230 xx_final = MIN(MAX(0, xx), xx_size);
1235 src_rect.h = height;
1240 // draw new (target) image to screen buffer
1241 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1243 if (xx_final < xx_size)
1245 src_rect.w = xx_size - xx_final;
1246 src_rect.h = height;
1248 // draw old (source) image to screen buffer (left side)
1250 src_rect.x = src_x + xx_final;
1253 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1255 // draw old (source) image to screen buffer (right side)
1257 src_rect.x = src_x + xx_size;
1258 dst_rect.x = dst_x + xx_size + xx_final;
1260 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1263 if (draw_border_function != NULL)
1264 draw_border_function();
1266 // only update the region of the screen that is affected from fading
1267 UpdateScreen_WithFrameDelay(&dst_rect2);
1270 else // fading in, fading out or cross-fading
1275 for (alpha = 0.0; alpha < 255.0;)
1277 time_last = time_current;
1278 time_current = SDL_GetTicks();
1279 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1280 alpha_final = MIN(MAX(0, alpha), 255);
1282 // draw existing (source) image to screen buffer
1283 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1285 // draw new (target) image to screen buffer using alpha blending
1286 SDLSetAlpha(surface_target, TRUE, alpha_final);
1287 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1289 if (draw_border_function != NULL)
1290 draw_border_function();
1292 // only update the region of the screen that is affected from fading
1293 UpdateScreen_WithFrameDelay(&dst_rect);
1298 Delay_WithScreenUpdates(post_delay);
1300 // restore function for drawing global masked border
1301 gfx.draw_global_border_function = draw_global_border_function;
1303 // after fading in, restore backbuffer (without animation graphics)
1304 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1305 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1308 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1309 int to_x, int to_y, Uint32 color)
1311 SDL_Surface *surface = dst_bitmap->surface;
1315 swap_numbers(&from_x, &to_x);
1318 swap_numbers(&from_y, &to_y);
1322 rect.w = (to_x - from_x + 1);
1323 rect.h = (to_y - from_y + 1);
1325 SDL_FillRect(surface, &rect, color);
1328 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1329 int to_x, int to_y, Uint32 color)
1331 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1334 #if ENABLE_UNUSED_CODE
1335 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1336 int num_points, Uint32 color)
1341 for (i = 0; i < num_points - 1; i++)
1343 for (x = 0; x < line_width; x++)
1345 for (y = 0; y < line_width; y++)
1347 int dx = x - line_width / 2;
1348 int dy = y - line_width / 2;
1350 if ((x == 0 && y == 0) ||
1351 (x == 0 && y == line_width - 1) ||
1352 (x == line_width - 1 && y == 0) ||
1353 (x == line_width - 1 && y == line_width - 1))
1356 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1357 points[i+1].x + dx, points[i+1].y + dy, color);
1364 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1366 SDL_Surface *surface = src_bitmap->surface;
1368 switch (surface->format->BytesPerPixel)
1370 case 1: // assuming 8-bpp
1372 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1376 case 2: // probably 15-bpp or 16-bpp
1378 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1382 case 3: // slow 24-bpp mode; usually not used
1385 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1389 shift = surface->format->Rshift;
1390 color |= *(pix + shift / 8) >> shift;
1391 shift = surface->format->Gshift;
1392 color |= *(pix + shift / 8) >> shift;
1393 shift = surface->format->Bshift;
1394 color |= *(pix + shift / 8) >> shift;
1400 case 4: // probably 32-bpp
1402 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1411 // ============================================================================
1412 // The following functions were taken from the SGE library
1413 // (SDL Graphics Extension Library) by Anders Lindström
1414 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1415 // ============================================================================
1417 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1419 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1421 switch (surface->format->BytesPerPixel)
1426 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1432 // Probably 15-bpp or 16-bpp
1433 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1439 // Slow 24-bpp mode, usually not used
1443 // Gack - slow, but endian correct
1444 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1445 shift = surface->format->Rshift;
1446 *(pix+shift/8) = color>>shift;
1447 shift = surface->format->Gshift;
1448 *(pix+shift/8) = color>>shift;
1449 shift = surface->format->Bshift;
1450 *(pix+shift/8) = color>>shift;
1457 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1465 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1466 Uint8 R, Uint8 G, Uint8 B)
1468 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1471 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1473 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1476 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1478 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1481 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1486 // Gack - slow, but endian correct
1487 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1488 shift = surface->format->Rshift;
1489 *(pix+shift/8) = color>>shift;
1490 shift = surface->format->Gshift;
1491 *(pix+shift/8) = color>>shift;
1492 shift = surface->format->Bshift;
1493 *(pix+shift/8) = color>>shift;
1496 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1498 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1501 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1503 switch (dest->format->BytesPerPixel)
1506 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1510 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1514 _PutPixel24(dest,x,y,color);
1518 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1524 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1526 if (SDL_MUSTLOCK(surface))
1528 if (SDL_LockSurface(surface) < 0)
1534 _PutPixel(surface, x, y, color);
1536 if (SDL_MUSTLOCK(surface))
1538 SDL_UnlockSurface(surface);
1543 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1544 Uint8 r, Uint8 g, Uint8 b)
1546 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1549 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1551 if (y >= 0 && y <= dest->h - 1)
1553 switch (dest->format->BytesPerPixel)
1556 return y*dest->pitch;
1560 return y*dest->pitch/2;
1564 return y*dest->pitch;
1568 return y*dest->pitch/4;
1576 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1579 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1581 switch (surface->format->BytesPerPixel)
1586 *((Uint8 *)surface->pixels + ypitch + x) = color;
1592 // Probably 15-bpp or 16-bpp
1593 *((Uint16 *)surface->pixels + ypitch + x) = color;
1599 // Slow 24-bpp mode, usually not used
1603 // Gack - slow, but endian correct
1604 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1605 shift = surface->format->Rshift;
1606 *(pix+shift/8) = color>>shift;
1607 shift = surface->format->Gshift;
1608 *(pix+shift/8) = color>>shift;
1609 shift = surface->format->Bshift;
1610 *(pix+shift/8) = color>>shift;
1617 *((Uint32 *)surface->pixels + ypitch + x) = color;
1624 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1629 if (SDL_MUSTLOCK(Surface))
1631 if (SDL_LockSurface(Surface) < 0)
1645 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1649 if (x2 > Surface->w - 1)
1650 x2 = Surface->w - 1;
1657 SDL_FillRect(Surface, &l, Color);
1659 if (SDL_MUSTLOCK(Surface))
1661 SDL_UnlockSurface(Surface);
1665 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1666 Uint8 R, Uint8 G, Uint8 B)
1668 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1671 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1684 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1688 if (x2 > Surface->w - 1)
1689 x2 = Surface->w - 1;
1696 SDL_FillRect(Surface, &l, Color);
1699 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1704 if (SDL_MUSTLOCK(Surface))
1706 if (SDL_LockSurface(Surface) < 0)
1720 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1724 if (y2 > Surface->h - 1)
1725 y2 = Surface->h - 1;
1732 SDL_FillRect(Surface, &l, Color);
1734 if (SDL_MUSTLOCK(Surface))
1736 SDL_UnlockSurface(Surface);
1740 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1741 Uint8 R, Uint8 G, Uint8 B)
1743 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1746 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1759 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1763 if (y2 > Surface->h - 1)
1764 y2 = Surface->h - 1;
1771 SDL_FillRect(Surface, &l, Color);
1775 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1776 Sint16 x2, Sint16 y2, Uint32 Color,
1777 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1780 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1785 sdx = (dx < 0) ? -1 : 1;
1786 sdy = (dy < 0) ? -1 : 1;
1798 for (x = 0; x < dx; x++)
1800 Callback(Surface, px, py, Color);
1814 for (y = 0; y < dy; y++)
1816 Callback(Surface, px, py, Color);
1831 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1832 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1833 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1836 sge_DoLine(Surface, X1, Y1, X2, Y2,
1837 SDL_MapRGB(Surface->format, R, G, B), Callback);
1841 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1844 if (SDL_MUSTLOCK(Surface))
1846 if (SDL_LockSurface(Surface) < 0)
1851 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1853 // unlock the display
1854 if (SDL_MUSTLOCK(Surface))
1856 SDL_UnlockSurface(Surface);
1861 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1862 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1864 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1868 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1870 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1874 // ----------------------------------------------------------------------------
1875 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1876 // ----------------------------------------------------------------------------
1878 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1879 int width, int height, Uint32 color)
1883 for (y = src_y; y < src_y + height; y++)
1885 for (x = src_x; x < src_x + width; x++)
1887 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1889 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1894 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1895 int src_x, int src_y, int width, int height,
1896 int dst_x, int dst_y)
1900 for (y = 0; y < height; y++)
1902 for (x = 0; x < width; x++)
1904 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1906 if (pixel != BLACK_PIXEL)
1907 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1913 // ============================================================================
1914 // The following functions were taken from the SDL_gfx library version 2.0.3
1915 // (Rotozoomer) by Andreas Schiffler
1916 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1917 // ============================================================================
1919 // ----------------------------------------------------------------------------
1922 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1923 // ----------------------------------------------------------------------------
1933 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1936 tColorRGBA *sp, *csp, *dp;
1940 sp = csp = (tColorRGBA *) src->pixels;
1941 dp = (tColorRGBA *) dst->pixels;
1942 dgap = dst->pitch - dst->w * 4;
1944 for (y = 0; y < dst->h; y++)
1948 for (x = 0; x < dst->w; x++)
1950 tColorRGBA *sp0 = sp;
1951 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1952 tColorRGBA *sp00 = &sp0[0];
1953 tColorRGBA *sp01 = &sp0[1];
1954 tColorRGBA *sp10 = &sp1[0];
1955 tColorRGBA *sp11 = &sp1[1];
1958 // create new color pixel from all four source color pixels
1959 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1960 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1961 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1962 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1967 // advance source pointers
1970 // advance destination pointer
1974 // advance source pointer
1975 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1977 // advance destination pointers
1978 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1984 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1986 int x, y, *sax, *say, *csax, *csay;
1988 tColorRGBA *sp, *csp, *csp0, *dp;
1991 // use specialized zoom function when scaling down to exactly half size
1992 if (src->w == 2 * dst->w &&
1993 src->h == 2 * dst->h)
1994 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1997 sx = (float) src->w / (float) dst->w;
1998 sy = (float) src->h / (float) dst->h;
2000 // allocate memory for row increments
2001 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2002 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2004 // precalculate row increments
2005 for (x = 0; x <= dst->w; x++)
2006 *csax++ = (int)(sx * x);
2008 for (y = 0; y <= dst->h; y++)
2009 *csay++ = (int)(sy * y);
2012 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2013 dp = (tColorRGBA *) dst->pixels;
2014 dgap = dst->pitch - dst->w * 4;
2017 for (y = 0; y < dst->h; y++)
2022 for (x = 0; x < dst->w; x++)
2027 // advance source pointers
2031 // advance destination pointer
2035 // advance source pointer
2037 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2039 // advance destination pointers
2040 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2049 // ----------------------------------------------------------------------------
2052 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2053 // ----------------------------------------------------------------------------
2055 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2057 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2058 Uint8 *sp, *dp, *csp;
2062 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2063 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2065 // allocate memory for row increments
2066 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2067 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2069 // precalculate row increments
2072 for (x = 0; x < dst->w; x++)
2075 *csax = (csx >> 16);
2082 for (y = 0; y < dst->h; y++)
2085 *csay = (csy >> 16);
2092 for (x = 0; x < dst->w; x++)
2100 for (y = 0; y < dst->h; y++)
2107 sp = csp = (Uint8 *) src->pixels;
2108 dp = (Uint8 *) dst->pixels;
2109 dgap = dst->pitch - dst->w;
2113 for (y = 0; y < dst->h; y++)
2117 for (x = 0; x < dst->w; x++)
2122 // advance source pointers
2126 // advance destination pointer
2130 // advance source pointer (for row)
2131 csp += ((*csay) * src->pitch);
2134 // advance destination pointers
2144 // ----------------------------------------------------------------------------
2147 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2148 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2149 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2150 // into a 32bit RGBA format on the fly.
2151 // ----------------------------------------------------------------------------
2153 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2155 SDL_Surface *zoom_src = NULL;
2156 SDL_Surface *zoom_dst = NULL;
2157 boolean is_converted = FALSE;
2164 // determine if source surface is 32 bit or 8 bit
2165 is_32bit = (src->format->BitsPerPixel == 32);
2167 if (is_32bit || src->format->BitsPerPixel == 8)
2169 // use source surface 'as is'
2174 // new source surface is 32 bit with a defined RGB ordering
2175 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2176 0x000000ff, 0x0000ff00, 0x00ff0000,
2177 (src->format->Amask ? 0xff000000 : 0));
2178 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2180 is_converted = TRUE;
2183 // allocate surface to completely contain the zoomed surface
2186 // target surface is 32 bit with source RGBA/ABGR ordering
2187 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2188 zoom_src->format->Rmask,
2189 zoom_src->format->Gmask,
2190 zoom_src->format->Bmask,
2191 zoom_src->format->Amask);
2195 // target surface is 8 bit
2196 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2200 // lock source surface
2201 SDL_LockSurface(zoom_src);
2203 // check which kind of surface we have
2206 // call the 32 bit transformation routine to do the zooming
2207 zoomSurfaceRGBA(zoom_src, zoom_dst);
2212 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2213 zoom_dst->format->palette->colors[i] =
2214 zoom_src->format->palette->colors[i];
2215 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2217 // call the 8 bit transformation routine to do the zooming
2218 zoomSurfaceY(zoom_src, zoom_dst);
2221 // unlock source surface
2222 SDL_UnlockSurface(zoom_src);
2224 // free temporary surface
2226 SDL_FreeSurface(zoom_src);
2228 // return destination surface
2232 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2234 SDL_Surface *new_surface;
2236 if (surface == NULL)
2239 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2240 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2242 // remove alpha channel from native non-transparent surface, if defined
2243 SDLSetAlpha(new_surface, FALSE, 0);
2245 // remove transparent color from native non-transparent surface, if defined
2246 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2251 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2253 Bitmap *dst_bitmap = CreateBitmapStruct();
2254 SDL_Surface *src_surface = src_bitmap->surface_masked;
2255 SDL_Surface *dst_surface;
2257 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2258 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2260 dst_bitmap->width = dst_width;
2261 dst_bitmap->height = dst_height;
2263 // create zoomed temporary surface from source surface
2264 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2266 // create native format destination surface from zoomed temporary surface
2267 SDLSetNativeSurface(&dst_surface);
2269 // set color key for zoomed surface from source surface, if defined
2270 if (SDLHasColorKey(src_surface))
2271 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2272 SDLGetColorKey(src_surface));
2274 // create native non-transparent surface for opaque blitting
2275 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2277 // set native transparent surface for masked blitting
2278 dst_bitmap->surface_masked = dst_surface;
2284 // ============================================================================
2285 // load image to bitmap
2286 // ============================================================================
2288 Bitmap *SDLLoadImage(char *filename)
2290 Bitmap *new_bitmap = CreateBitmapStruct();
2291 SDL_Surface *sdl_image_tmp;
2293 if (program.headless)
2295 // prevent sanity check warnings at later stage
2296 new_bitmap->width = new_bitmap->height = 1;
2301 print_timestamp_init("SDLLoadImage");
2303 print_timestamp_time(getBaseNamePtr(filename));
2305 // load image to temporary surface
2306 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2307 Error(ERR_EXIT, "IMG_Load('%s') failed: %s", getBaseNamePtr(filename),
2310 print_timestamp_time("IMG_Load");
2312 UPDATE_BUSY_STATE();
2314 // create native non-transparent surface for current image
2315 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2316 Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2318 print_timestamp_time("SDLGetNativeSurface (opaque)");
2320 UPDATE_BUSY_STATE();
2322 // set black pixel to transparent if no alpha channel / transparent color
2323 if (!SDLHasAlpha(sdl_image_tmp) &&
2324 !SDLHasColorKey(sdl_image_tmp))
2325 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2326 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2328 // create native transparent surface for current image
2329 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2330 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2332 print_timestamp_time("SDLGetNativeSurface (masked)");
2334 UPDATE_BUSY_STATE();
2336 // free temporary surface
2337 SDL_FreeSurface(sdl_image_tmp);
2339 new_bitmap->width = new_bitmap->surface->w;
2340 new_bitmap->height = new_bitmap->surface->h;
2342 print_timestamp_done("SDLLoadImage");
2348 // ----------------------------------------------------------------------------
2349 // custom cursor fuctions
2350 // ----------------------------------------------------------------------------
2352 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2354 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2355 cursor_info->width, cursor_info->height,
2356 cursor_info->hot_x, cursor_info->hot_y);
2359 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2361 static struct MouseCursorInfo *last_cursor_info = NULL;
2362 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2363 static SDL_Cursor *cursor_default = NULL;
2364 static SDL_Cursor *cursor_current = NULL;
2366 // if invoked for the first time, store the SDL default cursor
2367 if (cursor_default == NULL)
2368 cursor_default = SDL_GetCursor();
2370 // only create new cursor if cursor info (custom only) has changed
2371 if (cursor_info != NULL && cursor_info != last_cursor_info)
2373 cursor_current = create_cursor(cursor_info);
2374 last_cursor_info = cursor_info;
2377 // only set new cursor if cursor info (custom or NULL) has changed
2378 if (cursor_info != last_cursor_info2)
2379 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2381 last_cursor_info2 = cursor_info;
2385 // ============================================================================
2387 // ============================================================================
2389 void SDLOpenAudio(void)
2391 if (program.headless)
2394 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2396 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2400 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2401 AUDIO_NUM_CHANNELS_STEREO,
2402 setup.system.audio_fragment_size) < 0)
2404 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2408 audio.sound_available = TRUE;
2409 audio.music_available = TRUE;
2410 audio.loops_available = TRUE;
2411 audio.sound_enabled = TRUE;
2413 // set number of available mixer channels
2414 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2415 audio.music_channel = MUSIC_CHANNEL;
2416 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2418 Mixer_InitChannels();
2421 void SDLCloseAudio(void)
2424 Mix_HaltChannel(-1);
2427 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2431 // ============================================================================
2433 // ============================================================================
2435 void SDLWaitEvent(Event *event)
2437 SDL_WaitEvent(event);
2440 void SDLCorrectRawMousePosition(int *x, int *y)
2442 if (sdl_renderer == NULL)
2445 // this corrects the raw mouse position for logical screen size within event
2446 // filters (correction done later by SDL library when handling mouse events)
2449 float scale_x, scale_y;
2451 SDL_RenderGetViewport(sdl_renderer, &viewport);
2452 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2454 *x = (int)(*x / scale_x);
2455 *y = (int)(*y / scale_y);
2462 // ============================================================================
2463 // joystick functions
2464 // ============================================================================
2466 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2467 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2468 static int sdl_js_axis[MAX_PLAYERS][2];
2469 static int sdl_js_button[MAX_PLAYERS][2];
2470 static boolean sdl_is_controller[MAX_PLAYERS];
2472 void SDLClearJoystickState(void)
2476 for (i = 0; i < MAX_PLAYERS; i++)
2478 for (j = 0; j < 2; j++)
2480 sdl_js_axis_raw[i][j] = -1;
2481 sdl_js_axis[i][j] = 0;
2482 sdl_js_button[i][j] = 0;
2487 boolean SDLOpenJoystick(int nr)
2489 if (nr < 0 || nr >= MAX_PLAYERS)
2492 sdl_is_controller[nr] = SDL_IsGameController(nr);
2495 Error(ERR_DEBUG, "opening joystick %d (%s)",
2496 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2499 if (sdl_is_controller[nr])
2500 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2502 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2504 return (sdl_joystick[nr] != NULL);
2507 void SDLCloseJoystick(int nr)
2509 if (nr < 0 || nr >= MAX_PLAYERS)
2513 Error(ERR_DEBUG, "closing joystick %d", nr);
2516 if (sdl_is_controller[nr])
2517 SDL_GameControllerClose(sdl_joystick[nr]);
2519 SDL_JoystickClose(sdl_joystick[nr]);
2521 sdl_joystick[nr] = NULL;
2524 boolean SDLCheckJoystickOpened(int nr)
2526 if (nr < 0 || nr >= MAX_PLAYERS)
2529 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2532 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2534 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2535 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2536 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2537 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2539 if (nr < 0 || nr >= MAX_PLAYERS)
2545 // prevent (slightly jittering, but centered) axis A from resetting axis B
2546 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2547 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2550 sdl_js_axis[nr][axis_id] = axis_value;
2551 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2554 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2556 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2557 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2558 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2559 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2560 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2561 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2562 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2563 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2566 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2567 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2568 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2569 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2570 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2571 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2572 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2573 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2575 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2576 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2577 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2578 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2579 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2581 if (nr < 0 || nr >= MAX_PLAYERS)
2584 if (button_id == -1)
2587 sdl_js_button[nr][button_id] = button_state;
2590 void HandleJoystickEvent(Event *event)
2592 // when using joystick, disable overlay touch buttons
2593 runtime.uses_touch_device = FALSE;
2595 switch (event->type)
2597 case SDL_CONTROLLERDEVICEADDED:
2599 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2600 event->cdevice.which);
2605 case SDL_CONTROLLERDEVICEREMOVED:
2607 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2608 event->cdevice.which);
2613 case SDL_CONTROLLERAXISMOTION:
2615 Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2616 event->caxis.which, event->caxis.axis, event->caxis.value);
2618 setJoystickAxis(event->caxis.which,
2620 event->caxis.value);
2623 case SDL_CONTROLLERBUTTONDOWN:
2625 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2626 event->cbutton.which, event->cbutton.button);
2628 setJoystickButton(event->cbutton.which,
2629 event->cbutton.button,
2633 case SDL_CONTROLLERBUTTONUP:
2635 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2636 event->cbutton.which, event->cbutton.button);
2638 setJoystickButton(event->cbutton.which,
2639 event->cbutton.button,
2643 case SDL_JOYAXISMOTION:
2644 if (sdl_is_controller[event->jaxis.which])
2648 Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2649 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2651 if (event->jaxis.axis < 4)
2652 setJoystickAxis(event->jaxis.which,
2654 event->jaxis.value);
2657 case SDL_JOYBUTTONDOWN:
2658 if (sdl_is_controller[event->jaxis.which])
2662 Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2663 event->jbutton.which, event->jbutton.button);
2665 if (event->jbutton.button < 4)
2666 setJoystickButton(event->jbutton.which,
2667 event->jbutton.button,
2671 case SDL_JOYBUTTONUP:
2672 if (sdl_is_controller[event->jaxis.which])
2676 Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2677 event->jbutton.which, event->jbutton.button);
2679 if (event->jbutton.button < 4)
2680 setJoystickButton(event->jbutton.which,
2681 event->jbutton.button,
2690 void SDLInitJoysticks(void)
2692 static boolean sdl_joystick_subsystem_initialized = FALSE;
2693 boolean print_warning = !sdl_joystick_subsystem_initialized;
2694 char *mappings_file_base = getPath2(options.conf_directory,
2695 GAMECONTROLLER_BASENAME);
2696 char *mappings_file_user = getPath2(getUserGameDataDir(),
2697 GAMECONTROLLER_BASENAME);
2701 if (!sdl_joystick_subsystem_initialized)
2703 sdl_joystick_subsystem_initialized = TRUE;
2705 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2707 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2709 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2713 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2715 // the included game controller base mappings should always be found
2716 if (num_mappings == -1)
2717 Error(ERR_WARN, "no game controller base mappings found");
2720 Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2723 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2726 // the personal game controller user mappings may or may not be found
2727 if (num_mappings == -1)
2728 Error(ERR_WARN, "no game controller user mappings found");
2730 Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2732 Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2735 checked_free(mappings_file_base);
2736 checked_free(mappings_file_user);
2739 for (i = 0; i < SDL_NumJoysticks(); i++)
2741 const char *name, *type;
2743 if (SDL_IsGameController(i))
2745 name = SDL_GameControllerNameForIndex(i);
2746 type = "game controller";
2750 name = SDL_JoystickNameForIndex(i);
2754 Error(ERR_INFO, "- joystick %d (%s): '%s'",
2755 i, type, (name ? name : "(Unknown)"));
2760 // assign joysticks from configured to connected joystick for all players
2761 for (i = 0; i < MAX_PLAYERS; i++)
2763 // get configured joystick for this player
2764 char *device_name = setup.input[i].joy.device_name;
2765 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2767 if (joystick_nr >= SDL_NumJoysticks())
2769 if (setup.input[i].use_joystick && print_warning)
2770 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2775 // store configured joystick number for each player
2776 joystick.nr[i] = joystick_nr;
2779 // now open all connected joysticks (regardless if configured or not)
2780 for (i = 0; i < SDL_NumJoysticks(); i++)
2782 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2783 if (SDLCheckJoystickOpened(i))
2784 SDLCloseJoystick(i);
2786 if (SDLOpenJoystick(i))
2787 joystick.status = JOYSTICK_ACTIVATED;
2788 else if (print_warning)
2789 Error(ERR_WARN, "cannot open joystick %d", i);
2792 SDLClearJoystickState();
2795 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2797 if (nr < 0 || nr >= MAX_PLAYERS)
2801 *x = sdl_js_axis[nr][0];
2803 *y = sdl_js_axis[nr][1];
2806 *b1 = sdl_js_button[nr][0];
2808 *b2 = sdl_js_button[nr][1];
2814 // ============================================================================
2815 // touch input overlay functions
2816 // ============================================================================
2818 #if defined(USE_TOUCH_INPUT_OVERLAY)
2819 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2822 int grid_xsize = overlay.grid_xsize;
2823 int grid_ysize = overlay.grid_ysize;
2826 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2827 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2829 for (x = 0; x < grid_xsize; x++)
2831 rect.x = (x + 0) * video.screen_width / grid_xsize;
2832 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2834 for (y = 0; y < grid_ysize; y++)
2836 rect.y = (y + 0) * video.screen_height / grid_ysize;
2837 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2839 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2840 SDL_RenderDrawRect(sdl_renderer, &rect);
2844 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2847 static void RenderFillRectangle(int x, int y, int width, int height)
2849 SDL_Rect rect = { x, y, width, height };
2851 SDL_RenderFillRect(sdl_renderer, &rect);
2854 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2856 static int alpha_direction = 0;
2857 static int alpha_highlight = 0;
2858 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2859 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2861 int grid_xsize = overlay.grid_xsize;
2862 int grid_ysize = overlay.grid_ysize;
2865 if (alpha == alpha_max)
2867 if (alpha_direction < 0)
2869 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2871 if (alpha_highlight == 0)
2872 alpha_direction = 1;
2876 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2878 if (alpha_highlight == alpha_max)
2879 alpha_direction = -1;
2884 alpha_direction = 1;
2885 alpha_highlight = alpha;
2888 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2890 for (x = 0; x < grid_xsize; x++)
2892 for (y = 0; y < grid_ysize; y++)
2894 int grid_button = overlay.grid_button[x][y];
2895 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2896 int alpha_draw = alpha;
2897 int outline_border = MV_NONE;
2898 int border_size = 2;
2899 boolean draw_outlined = setup.touch.draw_outlined;
2900 boolean draw_pressed = setup.touch.draw_pressed;
2902 if (grid_button == CHAR_GRID_BUTTON_NONE)
2905 if (grid_button == overlay.grid_button_highlight)
2907 draw_outlined = FALSE;
2908 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2911 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2914 draw_outlined = FALSE;
2916 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2919 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2921 rect.x = (x + 0) * video.screen_width / grid_xsize;
2922 rect.y = (y + 0) * video.screen_height / grid_ysize;
2923 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2924 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2926 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2928 rect.x += border_size;
2929 rect.w -= border_size;
2931 outline_border |= MV_LEFT;
2934 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2936 rect.w -= border_size;
2938 outline_border |= MV_RIGHT;
2941 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2943 rect.y += border_size;
2944 rect.h -= border_size;
2946 outline_border |= MV_UP;
2949 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2951 rect.h -= border_size;
2953 outline_border |= MV_DOWN;
2958 int rect_x = rect.x +
2959 (outline_border & MV_LEFT ? border_size : 0);
2960 int rect_w = rect.w -
2961 (outline_border & MV_LEFT ? border_size : 0) -
2962 (outline_border & MV_RIGHT ? border_size : 0);
2964 if (outline_border & MV_LEFT)
2965 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2967 if (outline_border & MV_RIGHT)
2968 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2969 border_size, rect.h);
2971 if (outline_border & MV_UP)
2972 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2974 if (outline_border & MV_DOWN)
2975 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
2976 rect_w, border_size);
2980 SDL_RenderFillRect(sdl_renderer, &rect);
2985 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2988 static void DrawTouchInputOverlay(void)
2990 static boolean deactivated = TRUE;
2991 static boolean show_grid = FALSE;
2992 static int alpha = 0;
2993 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2994 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2995 boolean active = (overlay.enabled && overlay.active);
2997 if (!active && deactivated)
3002 if (alpha < alpha_max)
3003 alpha = MIN(alpha + alpha_step, alpha_max);
3005 deactivated = FALSE;
3009 alpha = MAX(0, alpha - alpha_step);
3015 if (overlay.show_grid)
3017 else if (deactivated)
3021 DrawTouchInputOverlay_ShowGrid(alpha);
3023 DrawTouchInputOverlay_ShowGridButtons(alpha);
3026 static void DrawTouchGadgetsOverlay(void)
3028 DrawGadgets_OverlayTouchButtons();