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 Debug("internal:frame", "FrameCounter == %d [%s]", 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 SDL_Rect src_rect_up = { 0, 0, video.width, video.height };
155 SDL_Rect dst_rect_up = dst_rect_screen;
157 if (video.shifted_up || video.shifted_up_delay)
159 int time_current = SDL_GetTicks();
160 int pos = video.shifted_up_pos;
161 int pos_last = video.shifted_up_pos_last;
163 if (!DelayReachedExt(&video.shifted_up_delay, video.shifted_up_delay_value,
166 int delay = time_current - video.shifted_up_delay;
167 int delay_value = video.shifted_up_delay_value;
169 pos = pos_last + (pos - pos_last) * delay / delay_value;
173 video.shifted_up_pos_last = pos;
174 video.shifted_up_delay = 0;
178 src_rect_up.h = video.height - pos;
179 dst_rect_up.h = video.height - pos;
181 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
182 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
184 src_rect2 = &src_rect_up;
185 dst_rect2 = &dst_rect_up;
189 src_rect1 = &src_rect_up;
190 dst_rect1 = &dst_rect_up;
195 // clear render target buffer
196 SDL_RenderClear(sdl_renderer);
198 // set renderer to use target texture for rendering
199 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
200 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
201 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
203 // copy backbuffer texture to render target buffer
204 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
205 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
207 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
208 FinalizeScreen(DRAW_TO_SCREEN);
210 // when using target texture, copy it to screen buffer
211 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
212 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
214 SDL_SetRenderTarget(sdl_renderer, NULL);
215 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
218 #if defined(USE_TOUCH_INPUT_OVERLAY)
219 // draw overlay graphics for touch device input, if needed
220 DrawTouchInputOverlay();
222 // draw overlay gadgets for touch device input, if needed
223 DrawTouchGadgetsOverlay();
226 // global synchronization point of the game to align video frame delay
227 if (with_frame_delay)
228 WaitUntilDelayReached(&video.frame_delay, video.frame_delay_value);
230 video.frame_counter++;
232 // show render target buffer on screen
233 SDL_RenderPresent(sdl_renderer);
236 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
238 PumpEvents(); // execute event filter actions while waiting
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_MAC)
268 char *filename = getCustomImageFilename(basename);
269 SDL_Surface *surface;
271 if (filename == NULL)
273 Warn("SDLSetWindowIcon(): cannot find file '%s'", basename);
278 if ((surface = IMG_Load(filename)) == NULL)
280 Warn("IMG_Load('%s') failed: %s", basename, SDL_GetError());
285 // set transparent color
286 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
287 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
289 SDL_SetWindowIcon(sdl_window, surface);
293 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
294 SDL_PixelFormat *format2)
296 return (format1->format == format2->format &&
297 format1->BitsPerPixel == format2->BitsPerPixel &&
298 format1->BytesPerPixel == format2->BytesPerPixel &&
299 format1->Rmask == format2->Rmask &&
300 format1->Gmask == format2->Gmask &&
301 format1->Bmask == format2->Bmask);
304 static void SDLCopyColorKey(SDL_Surface *src_surface, SDL_Surface *dst_surface)
309 // check if source surface has a color key
310 if (SDL_GetColorKey(src_surface, &color_key) == 0)
312 // get RGB values of color key of source surface
313 SDL_GetRGB(color_key, src_surface->format, &r, &g, &b);
315 // get color key from RGB values in destination surface format
316 color_key = SDL_MapRGB(dst_surface->format, r, g, b);
318 // set color key in destination surface
319 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL, color_key);
323 // unset color key in destination surface
324 SDL_SetColorKey(dst_surface, UNSET_TRANSPARENT_PIXEL, 0);
328 static boolean SDLHasColorKey(SDL_Surface *surface)
332 return (SDL_GetColorKey(surface, &color_key) == 0);
335 static boolean SDLHasAlpha(SDL_Surface *surface)
337 SDL_BlendMode blend_mode;
339 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
342 return (blend_mode == SDL_BLENDMODE_BLEND);
345 void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
347 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
349 SDL_SetSurfaceBlendMode(surface, blend_mode);
350 SDL_SetSurfaceAlphaMod(surface, alpha);
353 const char *SDLGetRendererName(void)
355 static SDL_RendererInfo renderer_info;
357 SDL_GetRendererInfo(sdl_renderer, &renderer_info);
359 return renderer_info.name;
362 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
364 SDL_PixelFormat format;
365 SDL_Surface *new_surface;
370 if (backbuffer && backbuffer->surface)
372 format = *backbuffer->surface->format;
373 format.Amask = surface->format->Amask; // keep alpha channel
377 format = *surface->format;
380 new_surface = SDL_ConvertSurface(surface, &format, 0);
382 if (new_surface == NULL)
383 Fail("SDL_ConvertSurface() failed: %s", SDL_GetError());
385 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
386 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
387 SDLCopyColorKey(surface, new_surface);
392 boolean SDLSetNativeSurface(SDL_Surface **surface)
394 SDL_Surface *new_surface;
396 if (surface == NULL ||
398 backbuffer == NULL ||
399 backbuffer->surface == NULL)
402 // if pixel format already optimized for destination surface, do nothing
403 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
406 new_surface = SDLGetNativeSurface(*surface);
408 SDL_FreeSurface(*surface);
410 *surface = new_surface;
415 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
417 if (program.headless)
420 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
423 Fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
428 void SDLCreateBitmapTextures(Bitmap *bitmap)
434 SDL_DestroyTexture(bitmap->texture);
435 if (bitmap->texture_masked)
436 SDL_DestroyTexture(bitmap->texture_masked);
438 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
439 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
442 void SDLFreeBitmapTextures(Bitmap *bitmap)
448 SDL_DestroyTexture(bitmap->texture);
449 if (bitmap->texture_masked)
450 SDL_DestroyTexture(bitmap->texture_masked);
452 bitmap->texture = NULL;
453 bitmap->texture_masked = NULL;
456 void SDLInitVideoDisplay(void)
458 // set hint to select render driver as specified in setup config file
459 if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
460 SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
462 // initialize SDL video
463 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
464 Fail("SDL_InitSubSystem() failed: %s", SDL_GetError());
466 // set default SDL depth
467 video.default_depth = 32; // (how to determine video depth in SDL2?)
469 // Code used with SDL 1.2:
470 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
473 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
475 if (program.headless)
478 video.window_scaling_percent = setup.window_scaling_percent;
479 video.window_scaling_quality = setup.window_scaling_quality;
481 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
483 // SDL 2.0: support for (desktop) fullscreen mode available
484 video.fullscreen_available = TRUE;
486 // open SDL video output device (window or fullscreen mode)
487 if (!SDLSetVideoMode(fullscreen))
488 Fail("setting video mode failed");
490 // !!! SDL2 can only set the window icon if the window already exists !!!
492 SDLSetWindowIcon(program.icon_filename);
494 // set window and icon title
498 static void SDLInitVideoBuffer_DrawBuffer(void)
500 /* SDL cannot directly draw to the visible video framebuffer like X11,
501 but always uses a backbuffer, which is then blitted to the visible
502 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
503 visible video framebuffer with 'SDL_Flip', if the hardware supports
504 this). Therefore do not use an additional backbuffer for drawing, but
505 use a symbolic buffer (distinguishable from the SDL backbuffer) called
506 'window', which indicates that the SDL backbuffer should be updated to
507 the visible video framebuffer when attempting to blit to it.
509 For convenience, it seems to be a good idea to create this symbolic
510 buffer 'window' at the same size as the SDL backbuffer. Although it
511 should never be drawn to directly, it would do no harm nevertheless. */
513 // create additional (symbolic) buffer for double-buffering
514 ReCreateBitmap(&window, video.width, video.height);
516 // create dummy drawing buffer for headless mode, if needed
517 if (program.headless)
518 ReCreateBitmap(&backbuffer, video.width, video.height);
521 void SDLInitVideoBuffer(boolean fullscreen)
523 SDLInitVideoBuffer_VideoBuffer(fullscreen);
524 SDLInitVideoBuffer_DrawBuffer();
527 static boolean SDLCreateScreen(boolean fullscreen)
529 SDL_Surface *new_surface = NULL;
531 int surface_flags_window = SURFACE_FLAGS;
532 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
535 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
537 video.vsync_mode = VSYNC_MODE_OFF;
539 if (!strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF))
541 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
542 video.vsync_mode = VSYNC_MODE_NORMAL;
545 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
546 _without_ enabling 2D/3D acceleration and/or guest additions installed,
547 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
548 it will try to use accelerated graphics and apparently fails miserably) */
549 int renderer_flags = SDL_RENDERER_SOFTWARE;
552 int width = video.width;
553 int height = video.height;
554 int screen_width = video.screen_width;
555 int screen_height = video.screen_height;
556 int surface_flags = (fullscreen ? surface_flags_fullscreen :
557 surface_flags_window);
559 // default window size is unscaled
560 video.window_width = screen_width;
561 video.window_height = screen_height;
563 // store if initial screen mode is fullscreen mode when changing screen size
564 video.fullscreen_initial = fullscreen;
566 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
568 video.window_width = window_scaling_factor * screen_width;
569 video.window_height = window_scaling_factor * screen_height;
571 if (sdl_texture_stream)
573 SDL_DestroyTexture(sdl_texture_stream);
574 sdl_texture_stream = NULL;
577 if (sdl_texture_target)
579 SDL_DestroyTexture(sdl_texture_target);
580 sdl_texture_target = NULL;
583 if (!(fullscreen && fullscreen_enabled))
587 SDL_DestroyRenderer(sdl_renderer);
593 SDL_DestroyWindow(sdl_window);
598 if (sdl_window == NULL)
599 sdl_window = SDL_CreateWindow(program.window_title,
600 SDL_WINDOWPOS_CENTERED,
601 SDL_WINDOWPOS_CENTERED,
606 if (sdl_window != NULL)
608 if (sdl_renderer == NULL)
609 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
611 if (sdl_renderer != NULL)
613 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
614 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
616 // required for setting adaptive vsync when using OpenGL renderer
617 SDLSetScreenVsyncMode(setup.vsync_mode);
619 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
620 SDL_PIXELFORMAT_ARGB8888,
621 SDL_TEXTUREACCESS_STREAMING,
624 if (SDL_RenderTargetSupported(sdl_renderer))
625 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
626 SDL_PIXELFORMAT_ARGB8888,
627 SDL_TEXTUREACCESS_TARGET,
630 if (sdl_texture_stream != NULL)
632 // use SDL default values for RGB masks and no alpha channel
633 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
635 if (new_surface == NULL)
636 Warn("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
640 Warn("SDL_CreateTexture() failed: %s", SDL_GetError());
645 Warn("SDL_CreateRenderer() failed: %s", SDL_GetError());
650 Warn("SDL_CreateWindow() failed: %s", SDL_GetError());
653 SDLSetScreenProperties();
655 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
656 if (new_surface != NULL)
657 fullscreen_enabled = fullscreen;
659 if (backbuffer == NULL)
660 backbuffer = CreateBitmapStruct();
662 backbuffer->width = video.width;
663 backbuffer->height = video.height;
665 if (backbuffer->surface)
666 SDL_FreeSurface(backbuffer->surface);
668 backbuffer->surface = new_surface;
670 return (new_surface != NULL);
673 boolean SDLSetVideoMode(boolean fullscreen)
675 boolean success = FALSE;
679 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
681 // switch display to fullscreen mode, if available
682 success = SDLCreateScreen(TRUE);
686 // switching display to fullscreen mode failed -- do not try it again
687 video.fullscreen_available = FALSE;
691 video.fullscreen_enabled = TRUE;
695 if ((!fullscreen && video.fullscreen_enabled) || !success)
697 // switch display to window mode
698 success = SDLCreateScreen(FALSE);
702 // switching display to window mode failed -- should not happen
706 video.fullscreen_enabled = FALSE;
707 video.window_scaling_percent = setup.window_scaling_percent;
708 video.window_scaling_quality = setup.window_scaling_quality;
710 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
714 SDLRedrawWindow(); // map window
719 void SDLSetWindowTitle(void)
721 if (sdl_window == NULL)
724 SDL_SetWindowTitle(sdl_window, program.window_title);
727 void SDLSetWindowScaling(int window_scaling_percent)
729 if (sdl_window == NULL)
732 float window_scaling_factor = (float)window_scaling_percent / 100;
733 int new_window_width = (int)(window_scaling_factor * video.screen_width);
734 int new_window_height = (int)(window_scaling_factor * video.screen_height);
736 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
738 video.window_scaling_percent = window_scaling_percent;
739 video.window_width = new_window_width;
740 video.window_height = new_window_height;
745 void SDLSetWindowScalingQuality(char *window_scaling_quality)
747 SDL_Texture *new_texture;
749 if (sdl_texture_stream == NULL)
752 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
754 new_texture = SDL_CreateTexture(sdl_renderer,
755 SDL_PIXELFORMAT_ARGB8888,
756 SDL_TEXTUREACCESS_STREAMING,
757 video.width, video.height);
759 if (new_texture != NULL)
761 SDL_DestroyTexture(sdl_texture_stream);
763 sdl_texture_stream = new_texture;
766 if (SDL_RenderTargetSupported(sdl_renderer))
767 new_texture = SDL_CreateTexture(sdl_renderer,
768 SDL_PIXELFORMAT_ARGB8888,
769 SDL_TEXTUREACCESS_TARGET,
770 video.width, video.height);
774 if (new_texture != NULL)
776 SDL_DestroyTexture(sdl_texture_target);
778 sdl_texture_target = new_texture;
783 video.window_scaling_quality = window_scaling_quality;
786 void SDLSetWindowFullscreen(boolean fullscreen)
788 if (sdl_window == NULL)
791 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
793 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
794 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
796 // if screen size was changed in fullscreen mode, correct desktop window size
797 if (!fullscreen && video.fullscreen_initial)
799 SDLSetWindowScaling(setup.window_scaling_percent);
800 SDL_SetWindowPosition(sdl_window,
801 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
803 video.fullscreen_initial = FALSE;
807 void SDLSetDisplaySize(void)
809 if (sdl_renderer != NULL)
813 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
815 video.display_width = w;
816 video.display_height = h;
819 Debug("video", "SDL renderer size: %d x %d",
820 video.display_width, video.display_height);
825 SDL_Rect display_bounds;
827 SDL_GetDisplayBounds(0, &display_bounds);
829 video.display_width = display_bounds.w;
830 video.display_height = display_bounds.h;
833 Debug("video", "SDL display size: %d x %d",
834 video.display_width, video.display_height);
839 void SDLSetScreenSizeAndOffsets(int width, int height)
841 // set default video screen size and offsets
842 video.screen_width = width;
843 video.screen_height = height;
844 video.screen_xoffset = 0;
845 video.screen_yoffset = 0;
847 #if defined(USE_COMPLETE_DISPLAY)
848 float ratio_video = (float) width / height;
849 float ratio_display = (float) video.display_width / video.display_height;
851 if (ratio_video != ratio_display)
853 // adjust drawable screen size to cover the whole device display
855 if (ratio_video < ratio_display)
856 video.screen_width *= ratio_display / ratio_video;
858 video.screen_height *= ratio_video / ratio_display;
860 video.screen_xoffset = (video.screen_width - width) / 2;
861 video.screen_yoffset = (video.screen_height - height) / 2;
864 Debug("video", "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
866 video.screen_width, video.screen_height,
867 ratio_video, ratio_display);
873 void SDLSetScreenSizeForRenderer(int width, int height)
875 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
878 void SDLSetScreenProperties(void)
881 SDLSetScreenSizeAndOffsets(video.width, video.height);
882 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
884 SetOverlayGridSizeAndButtons();
887 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
889 video.screen_rendering_mode =
890 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
891 SPECIAL_RENDERING_BITMAP :
892 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
893 SPECIAL_RENDERING_TARGET:
894 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
895 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
898 void SDLSetScreenVsyncMode(char *vsync_mode)
900 // changing vsync mode without re-creating renderer only supported by OpenGL
901 if (!strPrefixLower((char *)SDLGetRendererName(), "opengl"))
904 int interval = VSYNC_MODE_STR_TO_INT(vsync_mode);
905 int result = SDL_GL_SetSwapInterval(interval);
907 // if adaptive vsync requested, but not supported, retry with normal vsync
908 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
910 interval = VSYNC_MODE_NORMAL;
912 result = SDL_GL_SetSwapInterval(interval);
916 interval = VSYNC_MODE_OFF;
918 video.vsync_mode = interval;
921 void SDLRedrawWindow(void)
923 UpdateScreen_WithoutFrameDelay(NULL);
926 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
929 if (program.headless)
932 SDL_Surface *surface =
933 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
936 Fail("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
938 SDLSetNativeSurface(&surface);
940 bitmap->surface = surface;
943 void SDLFreeBitmapPointers(Bitmap *bitmap)
946 SDL_FreeSurface(bitmap->surface);
947 if (bitmap->surface_masked)
948 SDL_FreeSurface(bitmap->surface_masked);
950 bitmap->surface = NULL;
951 bitmap->surface_masked = NULL;
954 SDL_DestroyTexture(bitmap->texture);
955 if (bitmap->texture_masked)
956 SDL_DestroyTexture(bitmap->texture_masked);
958 bitmap->texture = NULL;
959 bitmap->texture_masked = NULL;
962 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
963 int src_x, int src_y, int width, int height,
964 int dst_x, int dst_y, int mask_mode)
966 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
967 SDL_Rect src_rect, dst_rect;
979 // if (src_bitmap != backbuffer || dst_bitmap != window)
980 if (!(src_bitmap == backbuffer && dst_bitmap == window))
981 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
982 src_bitmap->surface_masked : src_bitmap->surface),
983 &src_rect, real_dst_bitmap->surface, &dst_rect);
985 if (dst_bitmap == window)
986 UpdateScreen_WithFrameDelay(&dst_rect);
989 void SDLBlitTexture(Bitmap *bitmap,
990 int src_x, int src_y, int width, int height,
991 int dst_x, int dst_y, int mask_mode)
993 SDL_Texture *texture;
998 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1000 if (texture == NULL)
1006 src_rect.h = height;
1011 dst_rect.h = height;
1013 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1016 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1019 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1027 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1029 if (dst_bitmap == window)
1030 UpdateScreen_WithFrameDelay(&rect);
1033 void PrepareFadeBitmap(int draw_target)
1035 Bitmap *fade_bitmap =
1036 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1037 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1039 if (fade_bitmap == NULL)
1042 // copy backbuffer to fading buffer
1043 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1045 // add border and animations to fading buffer
1046 FinalizeScreen(draw_target);
1049 void SDLFadeRectangle(int x, int y, int width, int height,
1050 int fade_mode, int fade_delay, int post_delay,
1051 void (*draw_border_function)(void))
1053 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1054 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1055 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1056 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1057 SDL_Surface *surface_screen = backbuffer->surface;
1058 SDL_Rect src_rect, dst_rect;
1060 int src_x = x, src_y = y;
1061 int dst_x = x, dst_y = y;
1062 unsigned int time_last, time_current;
1064 // store function for drawing global masked border
1065 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1067 // deactivate drawing of global border while fading, if needed
1068 if (draw_border_function == NULL)
1069 gfx.draw_global_border_function = NULL;
1074 src_rect.h = height;
1078 dst_rect.w = width; // (ignored)
1079 dst_rect.h = height; // (ignored)
1081 dst_rect2 = dst_rect;
1083 // before fading in, store backbuffer (without animation graphics)
1084 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1085 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1087 // copy source and target surfaces to temporary surfaces for fading
1088 if (fade_mode & FADE_TYPE_TRANSFORM)
1090 // (source and target fading buffer already prepared)
1092 else if (fade_mode & FADE_TYPE_FADE_IN)
1094 // (target fading buffer already prepared)
1095 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1097 else // FADE_TYPE_FADE_OUT
1099 // (source fading buffer already prepared)
1100 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1103 time_current = SDL_GetTicks();
1105 if (fade_delay <= 0)
1107 // immediately draw final target frame without delay
1108 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1112 // when fading without delay, also skip post delay
1116 if (fade_mode == FADE_MODE_MELT)
1118 boolean done = FALSE;
1119 int melt_pixels = 2;
1120 int melt_columns = width / melt_pixels;
1121 int ypos[melt_columns];
1122 int max_steps = height / 8 + 32;
1127 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1129 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1131 ypos[0] = -GetSimpleRandom(16);
1133 for (i = 1 ; i < melt_columns; i++)
1135 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1137 ypos[i] = ypos[i - 1] + r;
1150 time_last = time_current;
1151 time_current = SDL_GetTicks();
1152 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1153 steps_final = MIN(MAX(0, steps), max_steps);
1157 done = (steps_done >= steps_final);
1159 for (i = 0 ; i < melt_columns; i++)
1167 else if (ypos[i] < height)
1172 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1174 if (ypos[i] + dy >= height)
1175 dy = height - ypos[i];
1177 // copy part of (appearing) target surface to upper area
1178 src_rect.x = src_x + i * melt_pixels;
1179 // src_rect.y = src_y + ypos[i];
1181 src_rect.w = melt_pixels;
1183 src_rect.h = ypos[i] + dy;
1185 dst_rect.x = dst_x + i * melt_pixels;
1186 // dst_rect.y = dst_y + ypos[i];
1189 if (steps_done >= steps_final)
1190 SDL_BlitSurface(surface_target, &src_rect,
1191 surface_screen, &dst_rect);
1195 // copy part of (disappearing) source surface to lower area
1196 src_rect.x = src_x + i * melt_pixels;
1198 src_rect.w = melt_pixels;
1199 src_rect.h = height - ypos[i];
1201 dst_rect.x = dst_x + i * melt_pixels;
1202 dst_rect.y = dst_y + ypos[i];
1204 if (steps_done >= steps_final)
1205 SDL_BlitSurface(surface_source, &src_rect,
1206 surface_screen, &dst_rect);
1212 src_rect.x = src_x + i * melt_pixels;
1214 src_rect.w = melt_pixels;
1215 src_rect.h = height;
1217 dst_rect.x = dst_x + i * melt_pixels;
1220 if (steps_done >= steps_final)
1221 SDL_BlitSurface(surface_target, &src_rect,
1222 surface_screen, &dst_rect);
1226 if (steps_done >= steps_final)
1228 if (draw_border_function != NULL)
1229 draw_border_function();
1231 UpdateScreen_WithFrameDelay(&dst_rect2);
1233 if (PendingEscapeKeyEvent())
1238 else if (fade_mode == FADE_MODE_CURTAIN)
1242 int xx_size = width / 2;
1244 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1246 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1248 for (xx = 0; xx < xx_size;)
1250 time_last = time_current;
1251 time_current = SDL_GetTicks();
1252 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1253 xx_final = MIN(MAX(0, xx), xx_size);
1258 src_rect.h = height;
1263 // draw new (target) image to screen buffer
1264 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1266 if (xx_final < xx_size)
1268 src_rect.w = xx_size - xx_final;
1269 src_rect.h = height;
1271 // draw old (source) image to screen buffer (left side)
1273 src_rect.x = src_x + xx_final;
1276 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1278 // draw old (source) image to screen buffer (right side)
1280 src_rect.x = src_x + xx_size;
1281 dst_rect.x = dst_x + xx_size + xx_final;
1283 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1286 if (draw_border_function != NULL)
1287 draw_border_function();
1289 // only update the region of the screen that is affected from fading
1290 UpdateScreen_WithFrameDelay(&dst_rect2);
1292 if (PendingEscapeKeyEvent())
1296 else // fading in, fading out or cross-fading
1301 for (alpha = 0.0; alpha < 255.0;)
1303 time_last = time_current;
1304 time_current = SDL_GetTicks();
1305 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1306 alpha_final = MIN(MAX(0, alpha), 255);
1308 // draw existing (source) image to screen buffer
1309 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1311 // draw new (target) image to screen buffer using alpha blending
1312 SDLSetAlpha(surface_target, TRUE, alpha_final);
1313 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1315 if (draw_border_function != NULL)
1316 draw_border_function();
1318 // only update the region of the screen that is affected from fading
1319 UpdateScreen_WithFrameDelay(&dst_rect);
1321 if (PendingEscapeKeyEvent())
1327 Delay_WithScreenUpdates(post_delay);
1329 // restore function for drawing global masked border
1330 gfx.draw_global_border_function = draw_global_border_function;
1332 // after fading in, restore backbuffer (without animation graphics)
1333 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1334 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1337 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1338 int to_x, int to_y, Uint32 color)
1340 SDL_Surface *surface = dst_bitmap->surface;
1344 swap_numbers(&from_x, &to_x);
1347 swap_numbers(&from_y, &to_y);
1351 rect.w = (to_x - from_x + 1);
1352 rect.h = (to_y - from_y + 1);
1354 SDL_FillRect(surface, &rect, color);
1357 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1358 int to_x, int to_y, Uint32 color)
1360 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1363 #if ENABLE_UNUSED_CODE
1364 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1365 int num_points, Uint32 color)
1370 for (i = 0; i < num_points - 1; i++)
1372 for (x = 0; x < line_width; x++)
1374 for (y = 0; y < line_width; y++)
1376 int dx = x - line_width / 2;
1377 int dy = y - line_width / 2;
1379 if ((x == 0 && y == 0) ||
1380 (x == 0 && y == line_width - 1) ||
1381 (x == line_width - 1 && y == 0) ||
1382 (x == line_width - 1 && y == line_width - 1))
1385 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1386 points[i+1].x + dx, points[i+1].y + dy, color);
1393 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1395 SDL_Surface *surface = src_bitmap->surface;
1397 switch (surface->format->BytesPerPixel)
1399 case 1: // assuming 8-bpp
1401 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1405 case 2: // probably 15-bpp or 16-bpp
1407 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1411 case 3: // slow 24-bpp mode; usually not used
1414 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1418 shift = surface->format->Rshift;
1419 color |= *(pix + shift / 8) >> shift;
1420 shift = surface->format->Gshift;
1421 color |= *(pix + shift / 8) >> shift;
1422 shift = surface->format->Bshift;
1423 color |= *(pix + shift / 8) >> shift;
1429 case 4: // probably 32-bpp
1431 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1440 // ============================================================================
1441 // The following functions were taken from the SGE library
1442 // (SDL Graphics Extension Library) by Anders Lindström
1443 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1444 // ============================================================================
1446 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1448 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1450 switch (surface->format->BytesPerPixel)
1455 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1461 // Probably 15-bpp or 16-bpp
1462 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1468 // Slow 24-bpp mode, usually not used
1472 // Gack - slow, but endian correct
1473 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1474 shift = surface->format->Rshift;
1475 *(pix+shift/8) = color>>shift;
1476 shift = surface->format->Gshift;
1477 *(pix+shift/8) = color>>shift;
1478 shift = surface->format->Bshift;
1479 *(pix+shift/8) = color>>shift;
1486 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1494 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1495 Uint8 R, Uint8 G, Uint8 B)
1497 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1500 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1502 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1505 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1507 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1510 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1515 // Gack - slow, but endian correct
1516 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1517 shift = surface->format->Rshift;
1518 *(pix+shift/8) = color>>shift;
1519 shift = surface->format->Gshift;
1520 *(pix+shift/8) = color>>shift;
1521 shift = surface->format->Bshift;
1522 *(pix+shift/8) = color>>shift;
1525 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1527 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1530 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1532 switch (dest->format->BytesPerPixel)
1535 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1539 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1543 _PutPixel24(dest,x,y,color);
1547 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1553 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1555 if (SDL_MUSTLOCK(surface))
1557 if (SDL_LockSurface(surface) < 0)
1563 _PutPixel(surface, x, y, color);
1565 if (SDL_MUSTLOCK(surface))
1567 SDL_UnlockSurface(surface);
1572 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1573 Uint8 r, Uint8 g, Uint8 b)
1575 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1578 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1580 if (y >= 0 && y <= dest->h - 1)
1582 switch (dest->format->BytesPerPixel)
1585 return y*dest->pitch;
1589 return y*dest->pitch/2;
1593 return y*dest->pitch;
1597 return y*dest->pitch/4;
1605 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1608 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1610 switch (surface->format->BytesPerPixel)
1615 *((Uint8 *)surface->pixels + ypitch + x) = color;
1621 // Probably 15-bpp or 16-bpp
1622 *((Uint16 *)surface->pixels + ypitch + x) = color;
1628 // Slow 24-bpp mode, usually not used
1632 // Gack - slow, but endian correct
1633 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1634 shift = surface->format->Rshift;
1635 *(pix+shift/8) = color>>shift;
1636 shift = surface->format->Gshift;
1637 *(pix+shift/8) = color>>shift;
1638 shift = surface->format->Bshift;
1639 *(pix+shift/8) = color>>shift;
1646 *((Uint32 *)surface->pixels + ypitch + x) = color;
1653 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1658 if (SDL_MUSTLOCK(Surface))
1660 if (SDL_LockSurface(Surface) < 0)
1674 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1678 if (x2 > Surface->w - 1)
1679 x2 = Surface->w - 1;
1686 SDL_FillRect(Surface, &l, Color);
1688 if (SDL_MUSTLOCK(Surface))
1690 SDL_UnlockSurface(Surface);
1694 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1695 Uint8 R, Uint8 G, Uint8 B)
1697 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1700 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1713 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1717 if (x2 > Surface->w - 1)
1718 x2 = Surface->w - 1;
1725 SDL_FillRect(Surface, &l, Color);
1728 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1733 if (SDL_MUSTLOCK(Surface))
1735 if (SDL_LockSurface(Surface) < 0)
1749 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1753 if (y2 > Surface->h - 1)
1754 y2 = Surface->h - 1;
1761 SDL_FillRect(Surface, &l, Color);
1763 if (SDL_MUSTLOCK(Surface))
1765 SDL_UnlockSurface(Surface);
1769 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1770 Uint8 R, Uint8 G, Uint8 B)
1772 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1775 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1788 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1792 if (y2 > Surface->h - 1)
1793 y2 = Surface->h - 1;
1800 SDL_FillRect(Surface, &l, Color);
1804 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1805 Sint16 x2, Sint16 y2, Uint32 Color,
1806 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1809 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1814 sdx = (dx < 0) ? -1 : 1;
1815 sdy = (dy < 0) ? -1 : 1;
1827 for (x = 0; x < dx; x++)
1829 Callback(Surface, px, py, Color);
1843 for (y = 0; y < dy; y++)
1845 Callback(Surface, px, py, Color);
1860 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1861 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1862 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1865 sge_DoLine(Surface, X1, Y1, X2, Y2,
1866 SDL_MapRGB(Surface->format, R, G, B), Callback);
1870 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1873 if (SDL_MUSTLOCK(Surface))
1875 if (SDL_LockSurface(Surface) < 0)
1880 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1882 // unlock the display
1883 if (SDL_MUSTLOCK(Surface))
1885 SDL_UnlockSurface(Surface);
1890 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1891 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1893 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1897 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1899 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1903 // ----------------------------------------------------------------------------
1904 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1905 // ----------------------------------------------------------------------------
1907 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1908 int width, int height, Uint32 color)
1912 for (y = src_y; y < src_y + height; y++)
1914 for (x = src_x; x < src_x + width; x++)
1916 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1918 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1923 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1924 int src_x, int src_y, int width, int height,
1925 int dst_x, int dst_y)
1929 for (y = 0; y < height; y++)
1931 for (x = 0; x < width; x++)
1933 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1935 if (pixel != BLACK_PIXEL)
1936 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1942 // ============================================================================
1943 // The following functions were taken from the SDL_gfx library version 2.0.3
1944 // (Rotozoomer) by Andreas Schiffler
1945 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1946 // ============================================================================
1948 // ----------------------------------------------------------------------------
1951 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1952 // ----------------------------------------------------------------------------
1962 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1965 tColorRGBA *sp, *csp, *dp;
1969 sp = csp = (tColorRGBA *) src->pixels;
1970 dp = (tColorRGBA *) dst->pixels;
1971 dgap = dst->pitch - dst->w * 4;
1973 for (y = 0; y < dst->h; y++)
1977 for (x = 0; x < dst->w; x++)
1979 tColorRGBA *sp0 = sp;
1980 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1981 tColorRGBA *sp00 = &sp0[0];
1982 tColorRGBA *sp01 = &sp0[1];
1983 tColorRGBA *sp10 = &sp1[0];
1984 tColorRGBA *sp11 = &sp1[1];
1987 // create new color pixel from all four source color pixels
1988 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1989 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1990 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1991 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1996 // advance source pointers
1999 // advance destination pointer
2003 // advance source pointer
2004 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2006 // advance destination pointers
2007 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2013 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2015 int x, y, *sax, *say, *csax, *csay;
2017 tColorRGBA *sp, *csp, *csp0, *dp;
2020 // use specialized zoom function when scaling down to exactly half size
2021 if (src->w == 2 * dst->w &&
2022 src->h == 2 * dst->h)
2023 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2026 sx = (float) src->w / (float) dst->w;
2027 sy = (float) src->h / (float) dst->h;
2029 // allocate memory for row increments
2030 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2031 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2033 // precalculate row increments
2034 for (x = 0; x <= dst->w; x++)
2035 *csax++ = (int)(sx * x);
2037 for (y = 0; y <= dst->h; y++)
2038 *csay++ = (int)(sy * y);
2041 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2042 dp = (tColorRGBA *) dst->pixels;
2043 dgap = dst->pitch - dst->w * 4;
2046 for (y = 0; y < dst->h; y++)
2051 for (x = 0; x < dst->w; x++)
2056 // advance source pointers
2060 // advance destination pointer
2064 // advance source pointer
2066 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2068 // advance destination pointers
2069 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2078 // ----------------------------------------------------------------------------
2081 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2082 // ----------------------------------------------------------------------------
2084 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2086 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2087 Uint8 *sp, *dp, *csp;
2091 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2092 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2094 // allocate memory for row increments
2095 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2096 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2098 // precalculate row increments
2101 for (x = 0; x < dst->w; x++)
2104 *csax = (csx >> 16);
2111 for (y = 0; y < dst->h; y++)
2114 *csay = (csy >> 16);
2121 for (x = 0; x < dst->w; x++)
2129 for (y = 0; y < dst->h; y++)
2136 sp = csp = (Uint8 *) src->pixels;
2137 dp = (Uint8 *) dst->pixels;
2138 dgap = dst->pitch - dst->w;
2142 for (y = 0; y < dst->h; y++)
2146 for (x = 0; x < dst->w; x++)
2151 // advance source pointers
2155 // advance destination pointer
2159 // advance source pointer (for row)
2160 csp += ((*csay) * src->pitch);
2163 // advance destination pointers
2173 // ----------------------------------------------------------------------------
2176 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2177 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2178 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2179 // into a 32bit RGBA format on the fly.
2180 // ----------------------------------------------------------------------------
2182 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2184 SDL_Surface *zoom_src = NULL;
2185 SDL_Surface *zoom_dst = NULL;
2186 boolean is_converted = FALSE;
2193 // determine if source surface is 32 bit or 8 bit
2194 is_32bit = (src->format->BitsPerPixel == 32);
2196 if (is_32bit || src->format->BitsPerPixel == 8)
2198 // use source surface 'as is'
2203 // new source surface is 32 bit with a defined RGB ordering
2204 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2205 0x000000ff, 0x0000ff00, 0x00ff0000,
2206 (src->format->Amask ? 0xff000000 : 0));
2207 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2209 is_converted = TRUE;
2212 // allocate surface to completely contain the zoomed surface
2215 // target surface is 32 bit with source RGBA/ABGR ordering
2216 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2217 zoom_src->format->Rmask,
2218 zoom_src->format->Gmask,
2219 zoom_src->format->Bmask,
2220 zoom_src->format->Amask);
2224 // target surface is 8 bit
2225 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2229 // lock source surface
2230 SDL_LockSurface(zoom_src);
2232 // check which kind of surface we have
2235 // call the 32 bit transformation routine to do the zooming
2236 zoomSurfaceRGBA(zoom_src, zoom_dst);
2241 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2242 zoom_dst->format->palette->colors[i] =
2243 zoom_src->format->palette->colors[i];
2244 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2246 // call the 8 bit transformation routine to do the zooming
2247 zoomSurfaceY(zoom_src, zoom_dst);
2250 // unlock source surface
2251 SDL_UnlockSurface(zoom_src);
2253 // free temporary surface
2255 SDL_FreeSurface(zoom_src);
2257 // return destination surface
2261 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2263 SDL_Surface *new_surface;
2265 if (surface == NULL)
2268 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2269 Fail("SDLGetNativeSurface() failed");
2271 // remove alpha channel from native non-transparent surface, if defined
2272 SDLSetAlpha(new_surface, FALSE, 0);
2274 // remove transparent color from native non-transparent surface, if defined
2275 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2280 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2282 Bitmap *dst_bitmap = CreateBitmapStruct();
2283 SDL_Surface *src_surface = src_bitmap->surface_masked;
2284 SDL_Surface *dst_surface;
2286 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2287 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2289 dst_bitmap->width = dst_width;
2290 dst_bitmap->height = dst_height;
2292 // create zoomed temporary surface from source surface
2293 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2295 // create native format destination surface from zoomed temporary surface
2296 SDLSetNativeSurface(&dst_surface);
2298 // set color key for zoomed surface from source surface, if defined
2299 if (SDLHasColorKey(src_surface))
2300 SDLCopyColorKey(src_surface, dst_surface);
2302 // create native non-transparent surface for opaque blitting
2303 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2305 // set native transparent surface for masked blitting
2306 dst_bitmap->surface_masked = dst_surface;
2312 // ============================================================================
2313 // load image to bitmap
2314 // ============================================================================
2316 Bitmap *SDLLoadImage(char *filename)
2318 Bitmap *new_bitmap = CreateBitmapStruct();
2319 SDL_Surface *sdl_image_tmp;
2321 if (program.headless)
2323 // prevent sanity check warnings at later stage
2324 new_bitmap->width = new_bitmap->height = 1;
2329 print_timestamp_init("SDLLoadImage");
2331 print_timestamp_time(getBaseNamePtr(filename));
2333 // load image to temporary surface
2334 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2335 Fail("IMG_Load('%s') failed: %s", getBaseNamePtr(filename), SDL_GetError());
2337 print_timestamp_time("IMG_Load");
2339 UPDATE_BUSY_STATE();
2341 // create native non-transparent surface for current image
2342 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2343 Fail("SDLGetOpaqueSurface() failed");
2345 print_timestamp_time("SDLGetNativeSurface (opaque)");
2347 UPDATE_BUSY_STATE();
2349 // set black pixel to transparent if no alpha channel / transparent color
2350 if (!SDLHasAlpha(sdl_image_tmp) &&
2351 !SDLHasColorKey(sdl_image_tmp))
2352 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2353 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2355 // create native transparent surface for current image
2356 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2357 Fail("SDLGetNativeSurface() failed");
2359 print_timestamp_time("SDLGetNativeSurface (masked)");
2361 UPDATE_BUSY_STATE();
2363 // free temporary surface
2364 SDL_FreeSurface(sdl_image_tmp);
2366 new_bitmap->width = new_bitmap->surface->w;
2367 new_bitmap->height = new_bitmap->surface->h;
2369 print_timestamp_done("SDLLoadImage");
2375 // ----------------------------------------------------------------------------
2376 // custom cursor fuctions
2377 // ----------------------------------------------------------------------------
2379 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2381 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2382 cursor_info->width, cursor_info->height,
2383 cursor_info->hot_x, cursor_info->hot_y);
2386 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2388 static struct MouseCursorInfo *last_cursor_info = NULL;
2389 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2390 static SDL_Cursor *cursor_default = NULL;
2391 static SDL_Cursor *cursor_current = NULL;
2393 // if invoked for the first time, store the SDL default cursor
2394 if (cursor_default == NULL)
2395 cursor_default = SDL_GetCursor();
2397 // only create new cursor if cursor info (custom only) has changed
2398 if (cursor_info != NULL && cursor_info != last_cursor_info)
2400 cursor_current = create_cursor(cursor_info);
2401 last_cursor_info = cursor_info;
2404 // only set new cursor if cursor info (custom or NULL) has changed
2405 if (cursor_info != last_cursor_info2)
2406 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2408 last_cursor_info2 = cursor_info;
2412 // ============================================================================
2414 // ============================================================================
2416 void SDLOpenAudio(void)
2418 if (program.headless)
2421 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2423 Warn("SDL_InitSubSystem() failed: %s", SDL_GetError());
2428 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2429 AUDIO_NUM_CHANNELS_STEREO,
2430 setup.system.audio_fragment_size) < 0)
2432 Warn("Mix_OpenAudio() failed: %s", SDL_GetError());
2437 audio.sound_available = TRUE;
2438 audio.music_available = TRUE;
2439 audio.loops_available = TRUE;
2440 audio.sound_enabled = TRUE;
2442 // set number of available mixer channels
2443 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2444 audio.music_channel = MUSIC_CHANNEL;
2445 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2447 Mixer_InitChannels();
2450 void SDLCloseAudio(void)
2453 Mix_HaltChannel(-1);
2456 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2460 // ============================================================================
2462 // ============================================================================
2464 void SDLWaitEvent(Event *event)
2466 SDL_WaitEvent(event);
2469 void SDLCorrectRawMousePosition(int *x, int *y)
2471 if (sdl_renderer == NULL)
2474 // this corrects the raw mouse position for logical screen size within event
2475 // filters (correction done later by SDL library when handling mouse events)
2478 float scale_x, scale_y;
2480 SDL_RenderGetViewport(sdl_renderer, &viewport);
2481 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2483 *x = (int)(*x / scale_x);
2484 *y = (int)(*y / scale_y);
2491 // ============================================================================
2492 // joystick functions
2493 // ============================================================================
2495 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2496 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2497 static int sdl_js_axis[MAX_PLAYERS][2];
2498 static int sdl_js_button[MAX_PLAYERS][2];
2499 static boolean sdl_is_controller[MAX_PLAYERS];
2501 void SDLClearJoystickState(void)
2505 for (i = 0; i < MAX_PLAYERS; i++)
2507 for (j = 0; j < 2; j++)
2509 sdl_js_axis_raw[i][j] = -1;
2510 sdl_js_axis[i][j] = 0;
2511 sdl_js_button[i][j] = 0;
2516 boolean SDLOpenJoystick(int nr)
2518 if (nr < 0 || nr >= MAX_PLAYERS)
2521 sdl_is_controller[nr] = SDL_IsGameController(nr);
2524 Debug("joystick", "opening joystick %d (%s)",
2525 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2528 if (sdl_is_controller[nr])
2529 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2531 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2533 return (sdl_joystick[nr] != NULL);
2536 void SDLCloseJoystick(int nr)
2538 if (nr < 0 || nr >= MAX_PLAYERS)
2542 Debug("joystick", "closing joystick %d", nr);
2545 if (sdl_is_controller[nr])
2546 SDL_GameControllerClose(sdl_joystick[nr]);
2548 SDL_JoystickClose(sdl_joystick[nr]);
2550 sdl_joystick[nr] = NULL;
2553 boolean SDLCheckJoystickOpened(int nr)
2555 if (nr < 0 || nr >= MAX_PLAYERS)
2558 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2561 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2563 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2564 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2565 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2566 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2568 if (nr < 0 || nr >= MAX_PLAYERS)
2574 // prevent (slightly jittering, but centered) axis A from resetting axis B
2575 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2576 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2579 sdl_js_axis[nr][axis_id] = axis_value;
2580 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2583 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2585 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2586 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2587 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2588 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2589 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2590 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2591 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2592 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2595 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2596 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2597 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2598 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2599 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2600 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2601 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2602 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2604 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2605 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2606 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2607 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2608 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2610 if (nr < 0 || nr >= MAX_PLAYERS)
2613 if (button_id == -1)
2616 sdl_js_button[nr][button_id] = button_state;
2619 void HandleJoystickEvent(Event *event)
2621 // when using joystick, disable overlay touch buttons
2622 runtime.uses_touch_device = FALSE;
2624 switch (event->type)
2626 case SDL_CONTROLLERDEVICEADDED:
2628 Debug("joystick", "SDL_CONTROLLERDEVICEADDED: device %d added",
2629 event->cdevice.which);
2634 case SDL_CONTROLLERDEVICEREMOVED:
2636 Debug("joystick", "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2637 event->cdevice.which);
2642 case SDL_CONTROLLERAXISMOTION:
2644 Debug("joystick", "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2645 event->caxis.which, event->caxis.axis, event->caxis.value);
2647 setJoystickAxis(event->caxis.which,
2649 event->caxis.value);
2652 case SDL_CONTROLLERBUTTONDOWN:
2654 Debug("joystick", "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2655 event->cbutton.which, event->cbutton.button);
2657 setJoystickButton(event->cbutton.which,
2658 event->cbutton.button,
2662 case SDL_CONTROLLERBUTTONUP:
2664 Debug("joystick", "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2665 event->cbutton.which, event->cbutton.button);
2667 setJoystickButton(event->cbutton.which,
2668 event->cbutton.button,
2672 case SDL_JOYAXISMOTION:
2673 if (sdl_is_controller[event->jaxis.which])
2677 Debug("joystick", "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2678 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2680 if (event->jaxis.axis < 4)
2681 setJoystickAxis(event->jaxis.which,
2683 event->jaxis.value);
2686 case SDL_JOYBUTTONDOWN:
2687 if (sdl_is_controller[event->jaxis.which])
2691 Debug("joystick", "SDL_JOYBUTTONDOWN: device %d, button %d",
2692 event->jbutton.which, event->jbutton.button);
2694 if (event->jbutton.button < 4)
2695 setJoystickButton(event->jbutton.which,
2696 event->jbutton.button,
2700 case SDL_JOYBUTTONUP:
2701 if (sdl_is_controller[event->jaxis.which])
2705 Debug("joystick", "SDL_JOYBUTTONUP: device %d, button %d",
2706 event->jbutton.which, event->jbutton.button);
2708 if (event->jbutton.button < 4)
2709 setJoystickButton(event->jbutton.which,
2710 event->jbutton.button,
2719 void SDLInitJoysticks(void)
2721 static boolean sdl_joystick_subsystem_initialized = FALSE;
2722 boolean print_warning = !sdl_joystick_subsystem_initialized;
2723 char *mappings_file_base = getPath2(options.conf_directory,
2724 GAMECONTROLLER_BASENAME);
2725 char *mappings_file_user = getPath2(getMainUserGameDataDir(),
2726 GAMECONTROLLER_BASENAME);
2730 if (!sdl_joystick_subsystem_initialized)
2732 sdl_joystick_subsystem_initialized = TRUE;
2734 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2736 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2737 Fail("SDL_Init() failed: %s", SDL_GetError());
2739 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2741 // the included game controller base mappings should always be found
2742 if (num_mappings == -1)
2743 Warn("no game controller base mappings found");
2746 Debug("joystick", "%d game controller base mapping(s) added",
2750 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2753 // the personal game controller user mappings may or may not be found
2754 if (num_mappings == -1)
2755 Warn("no game controller user mappings found");
2757 Debug("joystick", , "%d game controller user mapping(s) added",
2760 Debug("joystick", "%d joystick(s) found:", SDL_NumJoysticks());
2763 checked_free(mappings_file_base);
2764 checked_free(mappings_file_user);
2767 for (i = 0; i < SDL_NumJoysticks(); i++)
2769 const char *name, *type;
2771 if (SDL_IsGameController(i))
2773 name = SDL_GameControllerNameForIndex(i);
2774 type = "game controller";
2778 name = SDL_JoystickNameForIndex(i);
2782 Debug("joystick", "- joystick %d (%s): '%s'",
2783 i, type, (name ? name : "(Unknown)"));
2788 // assign joysticks from configured to connected joystick for all players
2789 for (i = 0; i < MAX_PLAYERS; i++)
2791 // get configured joystick for this player
2792 char *device_name = setup.input[i].joy.device_name;
2793 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2795 if (joystick_nr >= SDL_NumJoysticks())
2797 if (setup.input[i].use_joystick && print_warning)
2798 Warn("cannot find joystick %d", joystick_nr);
2803 // store configured joystick number for each player
2804 joystick.nr[i] = joystick_nr;
2807 // now open all connected joysticks (regardless if configured or not)
2808 for (i = 0; i < SDL_NumJoysticks(); i++)
2810 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2811 if (SDLCheckJoystickOpened(i))
2812 SDLCloseJoystick(i);
2814 if (SDLOpenJoystick(i))
2815 joystick.status = JOYSTICK_ACTIVATED;
2816 else if (print_warning)
2817 Warn("cannot open joystick %d", i);
2820 SDLClearJoystickState();
2823 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2825 if (nr < 0 || nr >= MAX_PLAYERS)
2829 *x = sdl_js_axis[nr][0];
2831 *y = sdl_js_axis[nr][1];
2834 *b1 = sdl_js_button[nr][0];
2836 *b2 = sdl_js_button[nr][1];
2842 // ============================================================================
2843 // touch input overlay functions
2844 // ============================================================================
2846 #if defined(USE_TOUCH_INPUT_OVERLAY)
2847 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2850 int grid_xsize = overlay.grid_xsize;
2851 int grid_ysize = overlay.grid_ysize;
2854 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2855 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2857 for (x = 0; x < grid_xsize; x++)
2859 rect.x = (x + 0) * video.screen_width / grid_xsize;
2860 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2862 for (y = 0; y < grid_ysize; y++)
2864 rect.y = (y + 0) * video.screen_height / grid_ysize;
2865 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2867 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2868 SDL_RenderDrawRect(sdl_renderer, &rect);
2872 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2875 static void RenderFillRectangle(int x, int y, int width, int height)
2877 SDL_Rect rect = { x, y, width, height };
2879 SDL_RenderFillRect(sdl_renderer, &rect);
2882 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2884 static int alpha_direction = 0;
2885 static int alpha_highlight = 0;
2886 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2887 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2889 int grid_xsize = overlay.grid_xsize;
2890 int grid_ysize = overlay.grid_ysize;
2893 if (alpha == alpha_max)
2895 if (alpha_direction < 0)
2897 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2899 if (alpha_highlight == 0)
2900 alpha_direction = 1;
2904 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2906 if (alpha_highlight == alpha_max)
2907 alpha_direction = -1;
2912 alpha_direction = 1;
2913 alpha_highlight = alpha;
2916 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2918 for (x = 0; x < grid_xsize; x++)
2920 for (y = 0; y < grid_ysize; y++)
2922 int grid_button = overlay.grid_button[x][y];
2923 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2924 int alpha_draw = alpha;
2925 int outline_border = MV_NONE;
2926 int border_size = 2;
2927 boolean draw_outlined = setup.touch.draw_outlined;
2928 boolean draw_pressed = setup.touch.draw_pressed;
2930 if (grid_button == CHAR_GRID_BUTTON_NONE)
2933 if (grid_button == overlay.grid_button_highlight)
2935 draw_outlined = FALSE;
2936 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2939 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2942 draw_outlined = FALSE;
2944 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2947 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2949 rect.x = (x + 0) * video.screen_width / grid_xsize;
2950 rect.y = (y + 0) * video.screen_height / grid_ysize;
2951 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2952 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2954 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2956 rect.x += border_size;
2957 rect.w -= border_size;
2959 outline_border |= MV_LEFT;
2962 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2964 rect.w -= border_size;
2966 outline_border |= MV_RIGHT;
2969 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2971 rect.y += border_size;
2972 rect.h -= border_size;
2974 outline_border |= MV_UP;
2977 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2979 rect.h -= border_size;
2981 outline_border |= MV_DOWN;
2986 int rect_x = rect.x +
2987 (outline_border & MV_LEFT ? border_size : 0);
2988 int rect_w = rect.w -
2989 (outline_border & MV_LEFT ? border_size : 0) -
2990 (outline_border & MV_RIGHT ? border_size : 0);
2992 if (outline_border & MV_LEFT)
2993 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2995 if (outline_border & MV_RIGHT)
2996 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2997 border_size, rect.h);
2999 if (outline_border & MV_UP)
3000 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
3002 if (outline_border & MV_DOWN)
3003 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
3004 rect_w, border_size);
3008 SDL_RenderFillRect(sdl_renderer, &rect);
3013 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3016 static void DrawTouchInputOverlay(void)
3018 static boolean deactivated = TRUE;
3019 static boolean show_grid = FALSE;
3020 static int alpha = 0;
3021 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3022 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3023 boolean active = (overlay.enabled && overlay.active);
3025 if (!active && deactivated)
3030 if (alpha < alpha_max)
3031 alpha = MIN(alpha + alpha_step, alpha_max);
3033 deactivated = FALSE;
3037 alpha = MAX(0, alpha - alpha_step);
3043 if (overlay.show_grid)
3045 else if (deactivated)
3049 DrawTouchInputOverlay_ShowGrid(alpha);
3051 DrawTouchInputOverlay_ShowGridButtons(alpha);
3054 static void DrawTouchGadgetsOverlay(void)
3056 DrawGadgets_OverlayTouchButtons();