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);
69 // copy envelope request to render target buffer, if needed (above all)
70 if (gfx.draw_envelope_request_function != NULL)
71 gfx.draw_envelope_request_function(draw_target);
73 // copy global animations to render target buffer, if defined (mouse pointer)
74 if (gfx.draw_global_anim_function != NULL)
75 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_3);
78 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
83 static DelayCounter update_screen_delay = { 50 }; // (milliseconds)
84 SDL_Surface *screen = backbuffer->surface;
86 if (limit_screen_updates &&
87 !DelayReached(&update_screen_delay))
90 LimitScreenUpdates(FALSE);
94 static int LastFrameCounter = 0;
95 boolean changed = (FrameCounter != LastFrameCounter);
97 Debug("internal:frame", "FrameCounter == %d [%s]", FrameCounter,
98 (changed ? "-" : "SAME FRAME UPDATED"));
100 LastFrameCounter = FrameCounter;
103 if (FrameCounter % 2)
109 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
110 gfx.final_screen_bitmap != NULL) // may not be initialized yet
112 // draw global animations using bitmaps instead of using textures
113 // to prevent texture scaling artefacts (this is potentially slower)
115 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
116 gfx.win_xsize, gfx.win_ysize, 0, 0);
118 FinalizeScreen(DRAW_TO_SCREEN);
120 screen = gfx.final_screen_bitmap->surface;
122 // force full window redraw
126 SDL_Texture *sdl_texture = sdl_texture_stream;
128 // deactivate use of target texture if render targets are not supported
129 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
130 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
131 sdl_texture_target == NULL)
132 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
134 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
135 sdl_texture = sdl_texture_target;
139 int bytes_x = screen->pitch / video.width;
140 int bytes_y = screen->pitch;
142 SDL_UpdateTexture(sdl_texture, rect,
143 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
148 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
151 int xoff = video.screen_xoffset;
152 int yoff = video.screen_yoffset;
153 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
154 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
155 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
157 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
158 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
159 dst_rect2 = &dst_rect_screen;
161 dst_rect1 = &dst_rect_screen;
163 #if defined(HAS_SCREEN_KEYBOARD)
164 SDL_Rect src_rect_up = { 0, 0, video.width, video.height };
165 SDL_Rect dst_rect_up = dst_rect_screen;
167 if (video.shifted_up || video.shifted_up_delay.count)
169 int time_current = SDL_GetTicks();
170 int pos = video.shifted_up_pos;
171 int pos_last = video.shifted_up_pos_last;
173 if (!DelayReachedExt(&video.shifted_up_delay, time_current))
175 int delay_count = time_current - video.shifted_up_delay.count;
176 int delay_value = video.shifted_up_delay.value;
178 pos = pos_last + (pos - pos_last) * delay_count / delay_value;
182 video.shifted_up_pos_last = pos;
183 video.shifted_up_delay.count = 0;
187 src_rect_up.h = video.height - pos;
188 dst_rect_up.h = video.height - pos;
190 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
191 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
193 src_rect2 = &src_rect_up;
194 dst_rect2 = &dst_rect_up;
198 src_rect1 = &src_rect_up;
199 dst_rect1 = &dst_rect_up;
204 // clear render target buffer
205 SDL_RenderClear(sdl_renderer);
207 // set renderer to use target texture for rendering
208 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
209 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
210 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
212 // copy backbuffer texture to render target buffer
213 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
214 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
216 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
217 FinalizeScreen(DRAW_TO_SCREEN);
219 // when using target texture, copy it to screen buffer
220 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
221 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
223 SDL_SetRenderTarget(sdl_renderer, NULL);
224 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
227 #if defined(USE_TOUCH_INPUT_OVERLAY)
228 // draw overlay graphics for touch device input, if needed
229 DrawTouchInputOverlay();
231 // draw overlay gadgets for touch device input, if needed
232 DrawTouchGadgetsOverlay();
235 // global synchronization point of the game to align video frame delay
236 if (with_frame_delay)
237 WaitUntilDelayReached(&video.frame_delay);
239 video.frame_counter++;
241 // show render target buffer on screen
242 SDL_RenderPresent(sdl_renderer);
245 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
247 PumpEvents(); // execute event filter actions while waiting
249 UpdateScreenExt(rect, TRUE);
252 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
254 UpdateScreenExt(rect, FALSE);
257 void Delay_WithScreenUpdates(unsigned int delay)
259 unsigned int time_current = SDL_GetTicks();
260 unsigned int time_delayed = time_current + delay;
262 while (time_current < time_delayed)
264 // updating the screen contains waiting for frame delay (non-busy)
265 UpdateScreen_WithFrameDelay(NULL);
267 time_current = SDL_GetTicks();
271 static void SDLSetWindowIcon(char *basename)
273 // (setting the window icon on Mac OS X would replace the high-quality
274 // dock icon with the currently smaller (and uglier) icon from file)
276 #if !defined(PLATFORM_MAC)
277 char *filename = getCustomImageFilename(basename);
278 SDL_Surface *surface;
280 if (filename == NULL)
282 Warn("SDLSetWindowIcon(): cannot find file '%s'", basename);
287 if ((surface = IMG_Load(filename)) == NULL)
289 Warn("IMG_Load('%s') failed: %s", basename, SDL_GetError());
294 // set transparent color
295 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
296 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
298 SDL_SetWindowIcon(sdl_window, surface);
302 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
303 SDL_PixelFormat *format2)
305 return (format1->format == format2->format &&
306 format1->BitsPerPixel == format2->BitsPerPixel &&
307 format1->BytesPerPixel == format2->BytesPerPixel &&
308 format1->Rmask == format2->Rmask &&
309 format1->Gmask == format2->Gmask &&
310 format1->Bmask == format2->Bmask);
313 static void SDLCopyColorKey(SDL_Surface *src_surface, SDL_Surface *dst_surface)
318 // check if source surface has a color key
319 if (SDL_GetColorKey(src_surface, &color_key) == 0)
321 // get RGB values of color key of source surface
322 SDL_GetRGB(color_key, src_surface->format, &r, &g, &b);
324 // get color key from RGB values in destination surface format
325 color_key = SDL_MapRGB(dst_surface->format, r, g, b);
327 // set color key in destination surface
328 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL, color_key);
332 // unset color key in destination surface
333 SDL_SetColorKey(dst_surface, UNSET_TRANSPARENT_PIXEL, 0);
337 static boolean SDLHasColorKey(SDL_Surface *surface)
341 return (SDL_GetColorKey(surface, &color_key) == 0);
344 static boolean SDLHasAlpha(SDL_Surface *surface)
346 SDL_BlendMode blend_mode;
348 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
351 return (blend_mode == SDL_BLENDMODE_BLEND);
354 static void SDLSetSurfaceAlpha(SDL_Surface *surface, boolean set, int alpha)
356 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
358 SDL_SetSurfaceBlendMode(surface, blend_mode);
359 SDL_SetSurfaceAlphaMod(surface, alpha);
362 static void SDLSetTextureAlpha(SDL_Texture *texture, boolean set, int alpha)
364 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
366 SDL_SetTextureBlendMode(texture, blend_mode);
367 SDL_SetTextureAlphaMod(texture, alpha);
370 static void SDLSetBitmapAlpha(Bitmap *bitmap, boolean is_texture,
373 int alpha_next_blit = bitmap->alpha_next_blit;
375 // alpha value must be requested every time before blitting, if needed
376 bitmap->alpha_next_blit = -1;
378 // nothing to do if requested alpha value is already set
379 if (bitmap->alpha[is_texture][is_masked] == alpha_next_blit)
382 // store requested alpha value for masked/unmasked surface/texture
383 bitmap->alpha[is_texture][is_masked] = alpha_next_blit;
385 // set blend mode if bitmap is masked or if alpha value is defined
386 boolean set_blend_mode = (is_masked || alpha_next_blit != -1);
388 // if alpha value is undefined, use default (opaque) alpha value
389 if (alpha_next_blit == -1)
390 alpha_next_blit = SDL_ALPHA_OPAQUE;
393 SDLSetTextureAlpha(is_masked ? bitmap->texture_masked : bitmap->texture,
394 set_blend_mode, alpha_next_blit);
396 SDLSetSurfaceAlpha(is_masked ? bitmap->surface_masked : bitmap->surface,
397 set_blend_mode, alpha_next_blit);
400 void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
402 SDLSetSurfaceAlpha(surface, set, alpha);
405 const char *SDLGetRendererName(void)
407 static SDL_RendererInfo renderer_info;
409 SDL_GetRendererInfo(sdl_renderer, &renderer_info);
411 return renderer_info.name;
414 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
416 SDL_PixelFormat format;
417 SDL_Surface *new_surface;
422 if (backbuffer && backbuffer->surface)
424 format = *backbuffer->surface->format;
425 format.Amask = surface->format->Amask; // keep alpha channel
429 format = *surface->format;
432 new_surface = SDL_ConvertSurface(surface, &format, 0);
434 if (new_surface == NULL)
435 Fail("SDL_ConvertSurface() failed: %s", SDL_GetError());
437 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
438 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
439 SDLCopyColorKey(surface, new_surface);
444 boolean SDLSetNativeSurface(SDL_Surface **surface)
446 SDL_Surface *new_surface;
448 if (surface == NULL ||
450 backbuffer == NULL ||
451 backbuffer->surface == NULL)
454 // if pixel format already optimized for destination surface, do nothing
455 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
458 new_surface = SDLGetNativeSurface(*surface);
460 SDL_FreeSurface(*surface);
462 *surface = new_surface;
467 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
469 if (program.headless)
472 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
475 Fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
480 void SDLCreateBitmapTextures(Bitmap *bitmap)
486 SDL_DestroyTexture(bitmap->texture);
487 if (bitmap->texture_masked)
488 SDL_DestroyTexture(bitmap->texture_masked);
490 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
491 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
494 void SDLFreeBitmapTextures(Bitmap *bitmap)
500 SDL_DestroyTexture(bitmap->texture);
501 if (bitmap->texture_masked)
502 SDL_DestroyTexture(bitmap->texture_masked);
504 bitmap->texture = NULL;
505 bitmap->texture_masked = NULL;
508 void SDLInitVideoDisplay(void)
510 // set hint to select render driver as specified in setup config file
511 if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
512 SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
514 // initialize SDL video
515 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
516 Fail("SDL_InitSubSystem() failed: %s", SDL_GetError());
518 // set default SDL depth
519 video.default_depth = 32; // (how to determine video depth in SDL2?)
521 // Code used with SDL 1.2:
522 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
525 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
527 if (program.headless)
530 video.window_scaling_percent = setup.window_scaling_percent;
531 video.window_scaling_quality = setup.window_scaling_quality;
533 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
535 // SDL 2.0: support for (desktop) fullscreen mode available
536 video.fullscreen_available = TRUE;
538 // open SDL video output device (window or fullscreen mode)
539 if (!SDLSetVideoMode(fullscreen))
540 Fail("setting video mode failed");
542 // !!! SDL2 can only set the window icon if the window already exists !!!
544 SDLSetWindowIcon(program.icon_filename);
546 // set window and icon title
550 static void SDLInitVideoBuffer_DrawBuffer(void)
552 /* SDL cannot directly draw to the visible video framebuffer like X11,
553 but always uses a backbuffer, which is then blitted to the visible
554 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
555 visible video framebuffer with 'SDL_Flip', if the hardware supports
556 this). Therefore do not use an additional backbuffer for drawing, but
557 use a symbolic buffer (distinguishable from the SDL backbuffer) called
558 'window', which indicates that the SDL backbuffer should be updated to
559 the visible video framebuffer when attempting to blit to it.
561 For convenience, it seems to be a good idea to create this symbolic
562 buffer 'window' at the same size as the SDL backbuffer. Although it
563 should never be drawn to directly, it would do no harm nevertheless. */
565 // create additional (symbolic) buffer for double-buffering
566 ReCreateBitmap(&window, video.width, video.height);
568 // create dummy drawing buffer for headless mode, if needed
569 if (program.headless)
570 ReCreateBitmap(&backbuffer, video.width, video.height);
573 void SDLInitVideoBuffer(boolean fullscreen)
575 SDLInitVideoBuffer_VideoBuffer(fullscreen);
576 SDLInitVideoBuffer_DrawBuffer();
579 static boolean SDLCreateScreen(boolean fullscreen)
581 SDL_Surface *new_surface = NULL;
583 int surface_flags_window = SURFACE_FLAGS;
584 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
587 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
589 video.vsync_mode = VSYNC_MODE_OFF;
591 if (!strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF))
593 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
594 video.vsync_mode = VSYNC_MODE_NORMAL;
597 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
598 _without_ enabling 2D/3D acceleration and/or guest additions installed,
599 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
600 it will try to use accelerated graphics and apparently fails miserably) */
601 int renderer_flags = SDL_RENDERER_SOFTWARE;
604 int width = video.width;
605 int height = video.height;
606 int screen_width = video.screen_width;
607 int screen_height = video.screen_height;
608 int surface_flags = (fullscreen ? surface_flags_fullscreen :
609 surface_flags_window);
610 int display_nr = options.display_nr;
612 // default window size is unscaled
613 video.window_width = screen_width;
614 video.window_height = screen_height;
616 // store if initial screen mode is fullscreen mode when changing screen size
617 video.fullscreen_initial = fullscreen;
619 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
621 video.window_width = window_scaling_factor * screen_width;
622 video.window_height = window_scaling_factor * screen_height;
624 if (sdl_texture_stream)
626 SDL_DestroyTexture(sdl_texture_stream);
627 sdl_texture_stream = NULL;
630 if (sdl_texture_target)
632 SDL_DestroyTexture(sdl_texture_target);
633 sdl_texture_target = NULL;
636 if (!(fullscreen && fullscreen_enabled))
640 SDL_DestroyRenderer(sdl_renderer);
646 SDL_SetWindowSize(sdl_window, video.window_width, video.window_height);
650 if (sdl_window == NULL)
651 sdl_window = SDL_CreateWindow(program.window_title,
652 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
653 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
658 if (sdl_window != NULL)
660 if (sdl_renderer == NULL)
661 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
663 if (sdl_renderer != NULL)
665 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
666 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
668 // required for setting adaptive vsync when using OpenGL renderer
669 SDLSetScreenVsyncMode(setup.vsync_mode);
671 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
672 SDL_PIXELFORMAT_ARGB8888,
673 SDL_TEXTUREACCESS_STREAMING,
676 if (SDL_RenderTargetSupported(sdl_renderer))
677 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
678 SDL_PIXELFORMAT_ARGB8888,
679 SDL_TEXTUREACCESS_TARGET,
682 if (sdl_texture_stream != NULL)
684 // use SDL default values for RGB masks and no alpha channel
685 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
687 if (new_surface == NULL)
688 Warn("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
692 Warn("SDL_CreateTexture() failed: %s", SDL_GetError());
697 Warn("SDL_CreateRenderer() failed: %s", SDL_GetError());
702 Warn("SDL_CreateWindow() failed: %s", SDL_GetError());
705 SDLSetScreenProperties();
707 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
708 if (new_surface != NULL)
709 fullscreen_enabled = fullscreen;
711 if (backbuffer == NULL)
712 backbuffer = CreateBitmapStruct();
714 backbuffer->width = video.width;
715 backbuffer->height = video.height;
717 if (backbuffer->surface)
718 SDL_FreeSurface(backbuffer->surface);
720 backbuffer->surface = new_surface;
722 return (new_surface != NULL);
725 boolean SDLSetVideoMode(boolean fullscreen)
727 boolean success = FALSE;
731 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
733 // switch display to fullscreen mode, if available
734 success = SDLCreateScreen(TRUE);
738 // switching display to fullscreen mode failed -- do not try it again
739 video.fullscreen_available = FALSE;
743 video.fullscreen_enabled = TRUE;
747 if ((!fullscreen && video.fullscreen_enabled) || !success)
749 // switch display to window mode
750 success = SDLCreateScreen(FALSE);
754 // switching display to window mode failed -- should not happen
758 video.fullscreen_enabled = FALSE;
759 video.window_scaling_percent = setup.window_scaling_percent;
760 video.window_scaling_quality = setup.window_scaling_quality;
762 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
766 SDLRedrawWindow(); // map window
771 void SDLSetWindowTitle(void)
773 if (sdl_window == NULL)
776 SDL_SetWindowTitle(sdl_window, program.window_title);
779 void SDLSetWindowScaling(int window_scaling_percent)
781 if (sdl_window == NULL)
784 float window_scaling_factor = (float)window_scaling_percent / 100;
785 int new_window_width = (int)(window_scaling_factor * video.screen_width);
786 int new_window_height = (int)(window_scaling_factor * video.screen_height);
788 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
790 video.window_scaling_percent = window_scaling_percent;
791 video.window_width = new_window_width;
792 video.window_height = new_window_height;
797 void SDLSetWindowScalingQuality(char *window_scaling_quality)
799 SDL_Texture *new_texture;
801 if (sdl_texture_stream == NULL)
804 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
806 new_texture = SDL_CreateTexture(sdl_renderer,
807 SDL_PIXELFORMAT_ARGB8888,
808 SDL_TEXTUREACCESS_STREAMING,
809 video.width, video.height);
811 if (new_texture != NULL)
813 SDL_DestroyTexture(sdl_texture_stream);
815 sdl_texture_stream = new_texture;
818 if (SDL_RenderTargetSupported(sdl_renderer))
819 new_texture = SDL_CreateTexture(sdl_renderer,
820 SDL_PIXELFORMAT_ARGB8888,
821 SDL_TEXTUREACCESS_TARGET,
822 video.width, video.height);
826 if (new_texture != NULL)
828 SDL_DestroyTexture(sdl_texture_target);
830 sdl_texture_target = new_texture;
835 video.window_scaling_quality = window_scaling_quality;
838 void SDLSetWindowFullscreen(boolean fullscreen)
840 if (sdl_window == NULL)
843 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
845 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
846 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
848 // if screen size was changed in fullscreen mode, correct desktop window size
849 if (!fullscreen && video.fullscreen_initial)
851 SDLSetWindowScaling(setup.window_scaling_percent);
852 SDL_SetWindowPosition(sdl_window,
853 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr),
854 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr));
856 video.fullscreen_initial = FALSE;
860 void SDLSetDisplaySize(void)
862 if (sdl_renderer != NULL)
866 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
868 video.display_width = w;
869 video.display_height = h;
872 Debug("video", "SDL renderer size: %d x %d",
873 video.display_width, video.display_height);
878 SDL_Rect display_bounds;
880 SDL_GetDisplayBounds(0, &display_bounds);
882 video.display_width = display_bounds.w;
883 video.display_height = display_bounds.h;
886 Debug("video", "SDL display size: %d x %d",
887 video.display_width, video.display_height);
892 void SDLSetScreenSizeAndOffsets(int width, int height)
894 // set default video screen size and offsets
895 video.screen_width = width;
896 video.screen_height = height;
897 video.screen_xoffset = 0;
898 video.screen_yoffset = 0;
900 #if defined(USE_COMPLETE_DISPLAY)
901 float ratio_video = (float) width / height;
902 float ratio_display = (float) video.display_width / video.display_height;
904 if (ratio_video != ratio_display)
906 // adjust drawable screen size to cover the whole device display
908 if (ratio_video < ratio_display)
909 video.screen_width *= ratio_display / ratio_video;
911 video.screen_height *= ratio_video / ratio_display;
913 video.screen_xoffset = (video.screen_width - width) / 2;
914 video.screen_yoffset = (video.screen_height - height) / 2;
917 Debug("video", "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
919 video.screen_width, video.screen_height,
920 ratio_video, ratio_display);
926 void SDLSetScreenSizeForRenderer(int width, int height)
928 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
931 void SDLSetScreenProperties(void)
934 SDLSetScreenSizeAndOffsets(video.width, video.height);
935 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
937 SetOverlayGridSizeAndButtons();
940 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
942 video.screen_rendering_mode =
943 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
944 SPECIAL_RENDERING_BITMAP :
945 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
946 SPECIAL_RENDERING_TARGET:
947 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
948 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
951 void SDLSetScreenVsyncMode(char *vsync_mode)
953 // changing vsync mode without re-creating renderer only supported by OpenGL
954 if (!strPrefixLower((char *)SDLGetRendererName(), "opengl"))
957 int interval = VSYNC_MODE_STR_TO_INT(vsync_mode);
958 int result = SDL_GL_SetSwapInterval(interval);
960 // if adaptive vsync requested, but not supported, retry with normal vsync
961 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
963 interval = VSYNC_MODE_NORMAL;
965 result = SDL_GL_SetSwapInterval(interval);
969 interval = VSYNC_MODE_OFF;
971 video.vsync_mode = interval;
974 void SDLRedrawWindow(void)
976 UpdateScreen_WithoutFrameDelay(NULL);
979 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
982 if (program.headless)
985 SDL_Surface *surface =
986 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
989 Fail("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
991 SDLSetNativeSurface(&surface);
993 bitmap->surface = surface;
996 void SDLFreeBitmapPointers(Bitmap *bitmap)
999 SDL_FreeSurface(bitmap->surface);
1000 if (bitmap->surface_masked)
1001 SDL_FreeSurface(bitmap->surface_masked);
1003 bitmap->surface = NULL;
1004 bitmap->surface_masked = NULL;
1006 if (bitmap->texture)
1007 SDL_DestroyTexture(bitmap->texture);
1008 if (bitmap->texture_masked)
1009 SDL_DestroyTexture(bitmap->texture_masked);
1011 bitmap->texture = NULL;
1012 bitmap->texture_masked = NULL;
1015 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1016 int src_x, int src_y, int width, int height,
1017 int dst_x, int dst_y, int mask_mode)
1019 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1020 SDL_Rect src_rect, dst_rect;
1025 src_rect.h = height;
1030 dst_rect.h = height;
1032 SDLSetBitmapAlpha(src_bitmap, FALSE, mask_mode == BLIT_MASKED);
1034 // if (src_bitmap != backbuffer || dst_bitmap != window)
1035 if (!(src_bitmap == backbuffer && dst_bitmap == window))
1036 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
1037 src_bitmap->surface_masked : src_bitmap->surface),
1038 &src_rect, real_dst_bitmap->surface, &dst_rect);
1040 if (dst_bitmap == window)
1041 UpdateScreen_WithFrameDelay(&dst_rect);
1044 void SDLBlitTexture(Bitmap *bitmap,
1045 int src_x, int src_y, int width, int height,
1046 int dst_x, int dst_y, int mask_mode)
1048 SDL_Texture *texture;
1053 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1055 if (texture == NULL)
1061 src_rect.h = height;
1066 dst_rect.h = height;
1068 SDLSetBitmapAlpha(bitmap, TRUE, mask_mode == BLIT_MASKED);
1070 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1073 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1076 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1084 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1086 if (dst_bitmap == window)
1087 UpdateScreen_WithFrameDelay(&rect);
1090 void PrepareFadeBitmap(int draw_target)
1092 Bitmap *fade_bitmap =
1093 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1094 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1096 if (fade_bitmap == NULL)
1099 // copy backbuffer to fading buffer
1100 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1102 // add border and animations to fading buffer
1103 FinalizeScreen(draw_target);
1106 void SDLFadeRectangle(int x, int y, int width, int height,
1107 int fade_mode, int fade_delay, int post_delay,
1108 void (*draw_border_function)(void))
1110 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1111 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1112 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1113 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1114 SDL_Surface *surface_screen = backbuffer->surface;
1115 SDL_Rect src_rect, dst_rect;
1117 int src_x = x, src_y = y;
1118 int dst_x = x, dst_y = y;
1119 unsigned int time_last, time_current;
1121 // store function for drawing global masked border
1122 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1124 // deactivate drawing of global border while fading, if needed
1125 if (draw_border_function == NULL)
1126 gfx.draw_global_border_function = NULL;
1131 src_rect.h = height;
1135 dst_rect.w = width; // (ignored)
1136 dst_rect.h = height; // (ignored)
1138 dst_rect2 = dst_rect;
1140 // before fading in, store backbuffer (without animation graphics)
1141 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1142 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1144 // copy source and target surfaces to temporary surfaces for fading
1145 if (fade_mode & FADE_TYPE_TRANSFORM)
1147 // (source and target fading buffer already prepared)
1149 else if (fade_mode & FADE_TYPE_FADE_IN)
1151 // (target fading buffer already prepared)
1152 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1154 else // FADE_TYPE_FADE_OUT
1156 // (source fading buffer already prepared)
1157 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1160 time_current = SDL_GetTicks();
1162 if (fade_delay <= 0)
1164 // immediately draw final target frame without delay
1165 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1169 // when fading without delay, also skip post delay
1173 if (fade_mode == FADE_MODE_MELT)
1175 boolean done = FALSE;
1176 int melt_pixels = 2;
1177 int melt_columns = width / melt_pixels;
1178 int ypos[melt_columns];
1179 int max_steps = height / 8 + 32;
1184 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1186 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1188 ypos[0] = -GetSimpleRandom(16);
1190 for (i = 1 ; i < melt_columns; i++)
1192 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1194 ypos[i] = ypos[i - 1] + r;
1207 time_last = time_current;
1208 time_current = SDL_GetTicks();
1209 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1210 steps_final = MIN(MAX(0, steps), max_steps);
1214 done = (steps_done >= steps_final);
1216 for (i = 0 ; i < melt_columns; i++)
1224 else if (ypos[i] < height)
1229 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1231 if (ypos[i] + dy >= height)
1232 dy = height - ypos[i];
1234 // copy part of (appearing) target surface to upper area
1235 src_rect.x = src_x + i * melt_pixels;
1236 // src_rect.y = src_y + ypos[i];
1238 src_rect.w = melt_pixels;
1240 src_rect.h = ypos[i] + dy;
1242 dst_rect.x = dst_x + i * melt_pixels;
1243 // dst_rect.y = dst_y + ypos[i];
1246 if (steps_done >= steps_final)
1247 SDL_BlitSurface(surface_target, &src_rect,
1248 surface_screen, &dst_rect);
1252 // copy part of (disappearing) source surface to lower area
1253 src_rect.x = src_x + i * melt_pixels;
1255 src_rect.w = melt_pixels;
1256 src_rect.h = height - ypos[i];
1258 dst_rect.x = dst_x + i * melt_pixels;
1259 dst_rect.y = dst_y + ypos[i];
1261 if (steps_done >= steps_final)
1262 SDL_BlitSurface(surface_source, &src_rect,
1263 surface_screen, &dst_rect);
1269 src_rect.x = src_x + i * melt_pixels;
1271 src_rect.w = melt_pixels;
1272 src_rect.h = height;
1274 dst_rect.x = dst_x + i * melt_pixels;
1277 if (steps_done >= steps_final)
1278 SDL_BlitSurface(surface_target, &src_rect,
1279 surface_screen, &dst_rect);
1283 if (steps_done >= steps_final)
1285 if (draw_border_function != NULL)
1286 draw_border_function();
1288 UpdateScreen_WithFrameDelay(&dst_rect2);
1290 if (PendingEscapeKeyEvent())
1295 else if (fade_mode == FADE_MODE_CURTAIN)
1299 int xx_size = width / 2;
1301 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1303 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1305 for (xx = 0; xx < xx_size;)
1307 time_last = time_current;
1308 time_current = SDL_GetTicks();
1309 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1310 xx_final = MIN(MAX(0, xx), xx_size);
1315 src_rect.h = height;
1320 // draw new (target) image to screen buffer
1321 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1323 if (xx_final < xx_size)
1325 src_rect.w = xx_size - xx_final;
1326 src_rect.h = height;
1328 // draw old (source) image to screen buffer (left side)
1330 src_rect.x = src_x + xx_final;
1333 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1335 // draw old (source) image to screen buffer (right side)
1337 src_rect.x = src_x + xx_size;
1338 dst_rect.x = dst_x + xx_size + xx_final;
1340 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1343 if (draw_border_function != NULL)
1344 draw_border_function();
1346 // only update the region of the screen that is affected from fading
1347 UpdateScreen_WithFrameDelay(&dst_rect2);
1349 if (PendingEscapeKeyEvent())
1353 else // fading in, fading out or cross-fading
1358 for (alpha = 0.0; alpha < 255.0;)
1360 time_last = time_current;
1361 time_current = SDL_GetTicks();
1362 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1363 alpha_final = MIN(MAX(0, alpha), 255);
1365 // draw existing (source) image to screen buffer
1366 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1368 // draw new (target) image to screen buffer using alpha blending
1369 SDLSetAlpha(surface_target, TRUE, alpha_final);
1370 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1372 if (draw_border_function != NULL)
1373 draw_border_function();
1375 // only update the region of the screen that is affected from fading
1376 UpdateScreen_WithFrameDelay(&dst_rect);
1378 if (PendingEscapeKeyEvent())
1384 Delay_WithScreenUpdates(post_delay);
1386 // restore function for drawing global masked border
1387 gfx.draw_global_border_function = draw_global_border_function;
1389 // after fading in, restore backbuffer (without animation graphics)
1390 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1391 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1394 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1395 int to_x, int to_y, Uint32 color)
1397 SDL_Surface *surface = dst_bitmap->surface;
1401 swap_numbers(&from_x, &to_x);
1404 swap_numbers(&from_y, &to_y);
1408 rect.w = (to_x - from_x + 1);
1409 rect.h = (to_y - from_y + 1);
1411 SDL_FillRect(surface, &rect, color);
1414 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1415 int to_x, int to_y, Uint32 color)
1417 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1420 #if ENABLE_UNUSED_CODE
1421 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1422 int num_points, Uint32 color)
1427 for (i = 0; i < num_points - 1; i++)
1429 for (x = 0; x < line_width; x++)
1431 for (y = 0; y < line_width; y++)
1433 int dx = x - line_width / 2;
1434 int dy = y - line_width / 2;
1436 if ((x == 0 && y == 0) ||
1437 (x == 0 && y == line_width - 1) ||
1438 (x == line_width - 1 && y == 0) ||
1439 (x == line_width - 1 && y == line_width - 1))
1442 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1443 points[i+1].x + dx, points[i+1].y + dy, color);
1450 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1452 SDL_Surface *surface = src_bitmap->surface;
1454 switch (surface->format->BytesPerPixel)
1456 case 1: // assuming 8-bpp
1458 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1462 case 2: // probably 15-bpp or 16-bpp
1464 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1468 case 3: // slow 24-bpp mode; usually not used
1471 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1475 shift = surface->format->Rshift;
1476 color |= *(pix + shift / 8) >> shift;
1477 shift = surface->format->Gshift;
1478 color |= *(pix + shift / 8) >> shift;
1479 shift = surface->format->Bshift;
1480 color |= *(pix + shift / 8) >> shift;
1486 case 4: // probably 32-bpp
1488 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1497 // ============================================================================
1498 // The following functions were taken from the SGE library
1499 // (SDL Graphics Extension Library) by Anders Lindström
1500 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1501 // ============================================================================
1503 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1505 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1507 switch (surface->format->BytesPerPixel)
1512 *((Uint8 *)surface->pixels + y * surface->pitch + x) = color;
1518 // Probably 15-bpp or 16-bpp
1519 *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x) = color;
1525 // Slow 24-bpp mode, usually not used
1529 // Gack - slow, but endian correct
1530 pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1531 shift = surface->format->Rshift;
1532 *(pix + shift / 8) = color>>shift;
1533 shift = surface->format->Gshift;
1534 *(pix + shift / 8) = color>>shift;
1535 shift = surface->format->Bshift;
1536 *(pix + shift / 8) = color>>shift;
1543 *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x) = color;
1551 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1552 Uint8 R, Uint8 G, Uint8 B)
1554 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1557 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1559 *((Uint8 *)surface->pixels + y * surface->pitch + x) = color;
1562 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1564 *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x) = color;
1567 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1572 // Gack - slow, but endian correct
1573 pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1574 shift = surface->format->Rshift;
1575 *(pix + shift / 8) = color>>shift;
1576 shift = surface->format->Gshift;
1577 *(pix + shift / 8) = color>>shift;
1578 shift = surface->format->Bshift;
1579 *(pix + shift / 8) = color>>shift;
1582 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1584 *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x) = color;
1587 static void _PutPixelX(SDL_Surface *dest, Sint16 x, Sint16 y, Uint32 color)
1589 switch (dest->format->BytesPerPixel)
1592 *((Uint8 *)dest->pixels + y * dest->pitch + x) = color;
1596 *((Uint16 *)dest->pixels + y * dest->pitch / 2 + x) = color;
1600 _PutPixel24(dest, x, y, color);
1604 *((Uint32 *)dest->pixels + y * dest->pitch / 4 + x) = color;
1610 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1612 if (SDL_MUSTLOCK(surface))
1614 if (SDL_LockSurface(surface) < 0)
1620 _PutPixel(surface, x, y, color);
1622 if (SDL_MUSTLOCK(surface))
1624 SDL_UnlockSurface(surface);
1629 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1630 Uint8 r, Uint8 g, Uint8 b)
1632 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1635 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1637 if (y >= 0 && y <= dest->h - 1)
1639 switch (dest->format->BytesPerPixel)
1642 return y * dest->pitch;
1646 return y * dest->pitch / 2;
1650 return y * dest->pitch;
1654 return y * dest->pitch / 4;
1662 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1665 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1667 switch (surface->format->BytesPerPixel)
1672 *((Uint8 *)surface->pixels + ypitch + x) = color;
1678 // Probably 15-bpp or 16-bpp
1679 *((Uint16 *)surface->pixels + ypitch + x) = color;
1685 // Slow 24-bpp mode, usually not used
1689 // Gack - slow, but endian correct
1690 pix = (Uint8 *)surface->pixels + ypitch + x * 3;
1691 shift = surface->format->Rshift;
1692 *(pix + shift / 8) = color>>shift;
1693 shift = surface->format->Gshift;
1694 *(pix + shift / 8) = color>>shift;
1695 shift = surface->format->Bshift;
1696 *(pix + shift / 8) = color>>shift;
1703 *((Uint32 *)surface->pixels + ypitch + x) = color;
1710 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1715 if (SDL_MUSTLOCK(Surface))
1717 if (SDL_LockSurface(Surface) < 0)
1731 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1735 if (x2 > Surface->w - 1)
1736 x2 = Surface->w - 1;
1743 SDL_FillRect(Surface, &l, Color);
1745 if (SDL_MUSTLOCK(Surface))
1747 SDL_UnlockSurface(Surface);
1751 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1752 Uint8 R, Uint8 G, Uint8 B)
1754 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1757 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1770 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1774 if (x2 > Surface->w - 1)
1775 x2 = Surface->w - 1;
1782 SDL_FillRect(Surface, &l, Color);
1785 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1790 if (SDL_MUSTLOCK(Surface))
1792 if (SDL_LockSurface(Surface) < 0)
1806 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1810 if (y2 > Surface->h - 1)
1811 y2 = Surface->h - 1;
1818 SDL_FillRect(Surface, &l, Color);
1820 if (SDL_MUSTLOCK(Surface))
1822 SDL_UnlockSurface(Surface);
1826 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1827 Uint8 R, Uint8 G, Uint8 B)
1829 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1832 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1845 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1849 if (y2 > Surface->h - 1)
1850 y2 = Surface->h - 1;
1857 SDL_FillRect(Surface, &l, Color);
1861 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1862 Sint16 x2, Sint16 y2, Uint32 Color,
1863 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1866 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1871 sdx = (dx < 0) ? -1 : 1;
1872 sdy = (dy < 0) ? -1 : 1;
1884 for (x = 0; x < dx; x++)
1886 Callback(Surface, px, py, Color);
1900 for (y = 0; y < dy; y++)
1902 Callback(Surface, px, py, Color);
1917 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1918 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1919 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1922 sge_DoLine(Surface, X1, Y1, X2, Y2,
1923 SDL_MapRGB(Surface->format, R, G, B), Callback);
1927 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1930 if (SDL_MUSTLOCK(Surface))
1932 if (SDL_LockSurface(Surface) < 0)
1937 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1939 // unlock the display
1940 if (SDL_MUSTLOCK(Surface))
1942 SDL_UnlockSurface(Surface);
1947 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1948 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1950 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1954 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1956 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1960 // ----------------------------------------------------------------------------
1961 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1962 // ----------------------------------------------------------------------------
1964 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1965 int src_x, int src_y, int width, int height,
1966 int dst_x, int dst_y)
1970 for (y = 0; y < height; y++)
1972 for (x = 0; x < width; x++)
1974 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1976 if (pixel != BLACK_PIXEL)
1977 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1983 // ============================================================================
1984 // The following functions were taken from the SDL_gfx library version 2.0.3
1985 // (Rotozoomer) by Andreas Schiffler
1986 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1987 // ============================================================================
1989 // ----------------------------------------------------------------------------
1992 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1993 // ----------------------------------------------------------------------------
2003 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
2006 tColorRGBA *sp, *csp, *dp;
2010 sp = csp = (tColorRGBA *) src->pixels;
2011 dp = (tColorRGBA *) dst->pixels;
2012 dgap = dst->pitch - dst->w * 4;
2014 for (y = 0; y < dst->h; y++)
2018 for (x = 0; x < dst->w; x++)
2020 tColorRGBA *sp0 = sp;
2021 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
2022 tColorRGBA *sp00 = &sp0[0];
2023 tColorRGBA *sp01 = &sp0[1];
2024 tColorRGBA *sp10 = &sp1[0];
2025 tColorRGBA *sp11 = &sp1[1];
2028 // create new color pixel from all four source color pixels
2029 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
2030 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
2031 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
2032 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
2037 // advance source pointers
2040 // advance destination pointer
2044 // advance source pointer
2045 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2047 // advance destination pointers
2048 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2054 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2056 int x, y, *sax, *say, *csax, *csay;
2058 tColorRGBA *sp, *csp, *csp0, *dp;
2061 // use specialized zoom function when scaling down to exactly half size
2062 if (src->w == 2 * dst->w &&
2063 src->h == 2 * dst->h)
2064 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2067 sx = (float) src->w / (float) dst->w;
2068 sy = (float) src->h / (float) dst->h;
2070 // allocate memory for row increments
2071 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2072 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2074 // precalculate row increments
2075 for (x = 0; x <= dst->w; x++)
2076 *csax++ = (int)(sx * x);
2078 for (y = 0; y <= dst->h; y++)
2079 *csay++ = (int)(sy * y);
2082 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2083 dp = (tColorRGBA *) dst->pixels;
2084 dgap = dst->pitch - dst->w * 4;
2087 for (y = 0; y < dst->h; y++)
2092 for (x = 0; x < dst->w; x++)
2097 // advance source pointers
2101 // advance destination pointer
2105 // advance source pointer
2107 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2109 // advance destination pointers
2110 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2119 // ----------------------------------------------------------------------------
2122 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2123 // ----------------------------------------------------------------------------
2125 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2127 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2128 Uint8 *sp, *dp, *csp;
2132 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2133 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2135 // allocate memory for row increments
2136 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2137 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2139 // precalculate row increments
2142 for (x = 0; x < dst->w; x++)
2145 *csax = (csx >> 16);
2152 for (y = 0; y < dst->h; y++)
2155 *csay = (csy >> 16);
2162 for (x = 0; x < dst->w; x++)
2170 for (y = 0; y < dst->h; y++)
2177 sp = csp = (Uint8 *) src->pixels;
2178 dp = (Uint8 *) dst->pixels;
2179 dgap = dst->pitch - dst->w;
2183 for (y = 0; y < dst->h; y++)
2187 for (x = 0; x < dst->w; x++)
2192 // advance source pointers
2196 // advance destination pointer
2200 // advance source pointer (for row)
2201 csp += ((*csay) * src->pitch);
2204 // advance destination pointers
2214 // ----------------------------------------------------------------------------
2217 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2218 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2219 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2220 // into a 32bit RGBA format on the fly.
2221 // ----------------------------------------------------------------------------
2223 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2225 SDL_Surface *zoom_src = NULL;
2226 SDL_Surface *zoom_dst = NULL;
2227 boolean is_converted = FALSE;
2234 // determine if source surface is 32 bit or 8 bit
2235 is_32bit = (src->format->BitsPerPixel == 32);
2237 if (is_32bit || src->format->BitsPerPixel == 8)
2239 // use source surface 'as is'
2244 // new source surface is 32 bit with a defined RGB ordering
2245 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2246 0x000000ff, 0x0000ff00, 0x00ff0000,
2247 (src->format->Amask ? 0xff000000 : 0));
2248 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2250 is_converted = TRUE;
2253 // allocate surface to completely contain the zoomed surface
2256 // target surface is 32 bit with source RGBA/ABGR ordering
2257 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2258 zoom_src->format->Rmask,
2259 zoom_src->format->Gmask,
2260 zoom_src->format->Bmask,
2261 zoom_src->format->Amask);
2265 // target surface is 8 bit
2266 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2270 // lock source surface
2271 SDL_LockSurface(zoom_src);
2273 // check which kind of surface we have
2276 // call the 32 bit transformation routine to do the zooming
2277 zoomSurfaceRGBA(zoom_src, zoom_dst);
2282 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2283 zoom_dst->format->palette->colors[i] =
2284 zoom_src->format->palette->colors[i];
2285 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2287 // call the 8 bit transformation routine to do the zooming
2288 zoomSurfaceY(zoom_src, zoom_dst);
2291 // unlock source surface
2292 SDL_UnlockSurface(zoom_src);
2294 // free temporary surface
2296 SDL_FreeSurface(zoom_src);
2298 // return destination surface
2302 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2304 SDL_Surface *new_surface;
2306 if (surface == NULL)
2309 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2310 Fail("SDLGetNativeSurface() failed");
2312 // remove alpha channel from native non-transparent surface, if defined
2313 SDLSetAlpha(new_surface, FALSE, 0);
2315 // remove transparent color from native non-transparent surface, if defined
2316 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2321 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2323 Bitmap *dst_bitmap = CreateBitmapStruct();
2324 SDL_Surface *src_surface = src_bitmap->surface_masked;
2325 SDL_Surface *dst_surface;
2327 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2328 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2330 dst_bitmap->width = dst_width;
2331 dst_bitmap->height = dst_height;
2333 // create zoomed temporary surface from source surface
2334 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2336 // create native format destination surface from zoomed temporary surface
2337 SDLSetNativeSurface(&dst_surface);
2339 // set color key for zoomed surface from source surface, if defined
2340 if (SDLHasColorKey(src_surface))
2341 SDLCopyColorKey(src_surface, dst_surface);
2343 // create native non-transparent surface for opaque blitting
2344 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2346 // set native transparent surface for masked blitting
2347 dst_bitmap->surface_masked = dst_surface;
2353 // ============================================================================
2354 // load image to bitmap
2355 // ============================================================================
2357 Bitmap *SDLLoadImage(char *filename)
2359 Bitmap *new_bitmap = CreateBitmapStruct();
2360 SDL_Surface *sdl_image_tmp;
2362 if (program.headless)
2364 // prevent sanity check warnings at later stage
2365 new_bitmap->width = new_bitmap->height = 1;
2370 print_timestamp_init("SDLLoadImage");
2372 print_timestamp_time(getBaseNamePtr(filename));
2374 // load image to temporary surface
2375 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2376 Fail("IMG_Load('%s') failed: %s", getBaseNamePtr(filename), SDL_GetError());
2378 print_timestamp_time("IMG_Load");
2380 UPDATE_BUSY_STATE();
2382 // create native non-transparent surface for current image
2383 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2384 Fail("SDLGetOpaqueSurface() failed");
2386 print_timestamp_time("SDLGetNativeSurface (opaque)");
2388 UPDATE_BUSY_STATE();
2390 // set black pixel to transparent if no alpha channel / transparent color
2391 if (!SDLHasAlpha(sdl_image_tmp) &&
2392 !SDLHasColorKey(sdl_image_tmp))
2393 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2394 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2396 // create native transparent surface for current image
2397 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2398 Fail("SDLGetNativeSurface() failed");
2400 print_timestamp_time("SDLGetNativeSurface (masked)");
2402 UPDATE_BUSY_STATE();
2404 // free temporary surface
2405 SDL_FreeSurface(sdl_image_tmp);
2407 new_bitmap->width = new_bitmap->surface->w;
2408 new_bitmap->height = new_bitmap->surface->h;
2410 print_timestamp_done("SDLLoadImage");
2416 // ----------------------------------------------------------------------------
2417 // custom cursor fuctions
2418 // ----------------------------------------------------------------------------
2420 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2422 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2423 cursor_info->width, cursor_info->height,
2424 cursor_info->hot_x, cursor_info->hot_y);
2427 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2429 static struct MouseCursorInfo *last_cursor_info = NULL;
2430 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2431 static SDL_Cursor *cursor_default = NULL;
2432 static SDL_Cursor *cursor_current = NULL;
2434 // if invoked for the first time, store the SDL default cursor
2435 if (cursor_default == NULL)
2436 cursor_default = SDL_GetCursor();
2438 // only create new cursor if cursor info (custom only) has changed
2439 if (cursor_info != NULL && cursor_info != last_cursor_info)
2441 cursor_current = create_cursor(cursor_info);
2442 last_cursor_info = cursor_info;
2445 // only set new cursor if cursor info (custom or NULL) has changed
2446 if (cursor_info != last_cursor_info2)
2447 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2449 last_cursor_info2 = cursor_info;
2453 // ============================================================================
2455 // ============================================================================
2457 void SDLOpenAudio(void)
2459 if (program.headless)
2462 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2464 Warn("SDL_InitSubSystem() failed: %s", SDL_GetError());
2469 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2470 AUDIO_NUM_CHANNELS_STEREO,
2471 setup.system.audio_fragment_size) < 0)
2473 Warn("Mix_OpenAudio() failed: %s", SDL_GetError());
2478 audio.sound_available = TRUE;
2479 audio.music_available = TRUE;
2480 audio.loops_available = TRUE;
2481 audio.sound_enabled = TRUE;
2483 // set number of available mixer channels
2484 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2485 audio.music_channel = MUSIC_CHANNEL;
2486 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2488 Mixer_InitChannels();
2491 void SDLCloseAudio(void)
2494 Mix_HaltChannel(-1);
2497 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2501 // ============================================================================
2503 // ============================================================================
2505 void SDLWaitEvent(Event *event)
2507 SDL_WaitEvent(event);
2510 void SDLCorrectRawMousePosition(int *x, int *y)
2512 if (sdl_renderer == NULL)
2515 // this corrects the raw mouse position for logical screen size within event
2516 // filters (correction done later by SDL library when handling mouse events)
2519 float scale_x, scale_y;
2521 SDL_RenderGetViewport(sdl_renderer, &viewport);
2522 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2524 *x = (int)(*x / scale_x);
2525 *y = (int)(*y / scale_y);
2532 // ============================================================================
2533 // joystick functions
2534 // ============================================================================
2536 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2537 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2538 static int sdl_js_axis[MAX_PLAYERS][2];
2539 static int sdl_js_button[MAX_PLAYERS][2];
2540 static boolean sdl_is_controller[MAX_PLAYERS];
2542 void SDLClearJoystickState(void)
2546 for (i = 0; i < MAX_PLAYERS; i++)
2548 for (j = 0; j < 2; j++)
2550 sdl_js_axis_raw[i][j] = -1;
2551 sdl_js_axis[i][j] = 0;
2552 sdl_js_button[i][j] = 0;
2557 boolean SDLOpenJoystick(int nr)
2559 if (nr < 0 || nr >= MAX_PLAYERS)
2562 sdl_is_controller[nr] = SDL_IsGameController(nr);
2565 Debug("joystick", "opening joystick %d (%s)",
2566 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2569 if (sdl_is_controller[nr])
2570 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2572 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2574 return (sdl_joystick[nr] != NULL);
2577 void SDLCloseJoystick(int nr)
2579 if (nr < 0 || nr >= MAX_PLAYERS)
2583 Debug("joystick", "closing joystick %d", nr);
2586 if (sdl_is_controller[nr])
2587 SDL_GameControllerClose(sdl_joystick[nr]);
2589 SDL_JoystickClose(sdl_joystick[nr]);
2591 sdl_joystick[nr] = NULL;
2594 boolean SDLCheckJoystickOpened(int nr)
2596 if (nr < 0 || nr >= MAX_PLAYERS)
2599 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2602 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2604 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2605 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2606 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2607 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2609 if (nr < 0 || nr >= MAX_PLAYERS)
2615 // prevent (slightly jittering, but centered) axis A from resetting axis B
2616 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2617 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2620 sdl_js_axis[nr][axis_id] = axis_value;
2621 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2624 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2626 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2627 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2628 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2629 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2630 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2631 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2632 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2633 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2636 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2637 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2638 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2639 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2640 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2641 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2642 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2643 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2645 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2646 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2647 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2648 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2649 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2651 if (nr < 0 || nr >= MAX_PLAYERS)
2654 if (button_id == -1)
2657 sdl_js_button[nr][button_id] = button_state;
2660 void HandleJoystickEvent(Event *event)
2662 // when using joystick, disable overlay touch buttons
2663 runtime.uses_touch_device = FALSE;
2665 switch (event->type)
2667 case SDL_CONTROLLERDEVICEADDED:
2669 Debug("joystick", "SDL_CONTROLLERDEVICEADDED: device %d added",
2670 event->cdevice.which);
2675 case SDL_CONTROLLERDEVICEREMOVED:
2677 Debug("joystick", "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2678 event->cdevice.which);
2683 case SDL_CONTROLLERAXISMOTION:
2685 Debug("joystick", "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2686 event->caxis.which, event->caxis.axis, event->caxis.value);
2688 setJoystickAxis(event->caxis.which,
2690 event->caxis.value);
2693 case SDL_CONTROLLERBUTTONDOWN:
2695 Debug("joystick", "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2696 event->cbutton.which, event->cbutton.button);
2698 setJoystickButton(event->cbutton.which,
2699 event->cbutton.button,
2703 case SDL_CONTROLLERBUTTONUP:
2705 Debug("joystick", "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2706 event->cbutton.which, event->cbutton.button);
2708 setJoystickButton(event->cbutton.which,
2709 event->cbutton.button,
2713 case SDL_JOYAXISMOTION:
2714 if (sdl_is_controller[event->jaxis.which])
2718 Debug("joystick", "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2719 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2721 if (event->jaxis.axis < 4)
2722 setJoystickAxis(event->jaxis.which,
2724 event->jaxis.value);
2727 case SDL_JOYBUTTONDOWN:
2728 if (sdl_is_controller[event->jaxis.which])
2732 Debug("joystick", "SDL_JOYBUTTONDOWN: device %d, button %d",
2733 event->jbutton.which, event->jbutton.button);
2735 if (event->jbutton.button < 4)
2736 setJoystickButton(event->jbutton.which,
2737 event->jbutton.button,
2741 case SDL_JOYBUTTONUP:
2742 if (sdl_is_controller[event->jaxis.which])
2746 Debug("joystick", "SDL_JOYBUTTONUP: device %d, button %d",
2747 event->jbutton.which, event->jbutton.button);
2749 if (event->jbutton.button < 4)
2750 setJoystickButton(event->jbutton.which,
2751 event->jbutton.button,
2760 void SDLInitJoysticks(void)
2762 static boolean sdl_joystick_subsystem_initialized = FALSE;
2763 boolean print_warning = !sdl_joystick_subsystem_initialized;
2764 char *mappings_file_base = getPath2(options.conf_directory,
2765 GAMECONTROLLER_BASENAME);
2766 char *mappings_file_user = getPath2(getMainUserGameDataDir(),
2767 GAMECONTROLLER_BASENAME);
2771 if (!sdl_joystick_subsystem_initialized)
2773 sdl_joystick_subsystem_initialized = TRUE;
2775 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2777 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2778 Fail("SDL_Init() failed: %s", SDL_GetError());
2780 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2782 // the included game controller base mappings should always be found
2783 if (num_mappings == -1)
2784 Warn("no game controller base mappings found");
2787 Debug("joystick", "%d game controller base mapping(s) added",
2791 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2794 // the personal game controller user mappings may or may not be found
2795 if (num_mappings == -1)
2796 Warn("no game controller user mappings found");
2798 Debug("joystick", , "%d game controller user mapping(s) added",
2801 Debug("joystick", "%d joystick(s) found:", SDL_NumJoysticks());
2804 checked_free(mappings_file_base);
2805 checked_free(mappings_file_user);
2808 for (i = 0; i < SDL_NumJoysticks(); i++)
2810 const char *name, *type;
2812 if (SDL_IsGameController(i))
2814 name = SDL_GameControllerNameForIndex(i);
2815 type = "game controller";
2819 name = SDL_JoystickNameForIndex(i);
2823 Debug("joystick", "- joystick %d (%s): '%s'",
2824 i, type, (name ? name : "(Unknown)"));
2829 // assign joysticks from configured to connected joystick for all players
2830 for (i = 0; i < MAX_PLAYERS; i++)
2832 // get configured joystick for this player
2833 char *device_name = setup.input[i].joy.device_name;
2834 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2836 if (joystick_nr >= SDL_NumJoysticks())
2838 if (setup.input[i].use_joystick && print_warning)
2839 Warn("cannot find joystick %d", joystick_nr);
2844 // store configured joystick number for each player
2845 joystick.nr[i] = joystick_nr;
2848 // now open all connected joysticks (regardless if configured or not)
2849 for (i = 0; i < SDL_NumJoysticks(); i++)
2851 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2852 if (SDLCheckJoystickOpened(i))
2853 SDLCloseJoystick(i);
2855 if (SDLOpenJoystick(i))
2856 joystick.status = JOYSTICK_ACTIVATED;
2857 else if (print_warning)
2858 Warn("cannot open joystick %d", i);
2861 SDLClearJoystickState();
2864 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2866 if (nr < 0 || nr >= MAX_PLAYERS)
2870 *x = sdl_js_axis[nr][0];
2872 *y = sdl_js_axis[nr][1];
2875 *b1 = sdl_js_button[nr][0];
2877 *b2 = sdl_js_button[nr][1];
2883 // ============================================================================
2884 // touch input overlay functions
2885 // ============================================================================
2887 #if defined(USE_TOUCH_INPUT_OVERLAY)
2888 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2891 int grid_xsize = overlay.grid_xsize;
2892 int grid_ysize = overlay.grid_ysize;
2895 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2896 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2898 for (x = 0; x < grid_xsize; x++)
2900 rect.x = (x + 0) * video.screen_width / grid_xsize;
2901 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2903 for (y = 0; y < grid_ysize; y++)
2905 rect.y = (y + 0) * video.screen_height / grid_ysize;
2906 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2908 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2909 SDL_RenderDrawRect(sdl_renderer, &rect);
2913 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2916 static void RenderFillRectangle(int x, int y, int width, int height)
2918 SDL_Rect rect = { x, y, width, height };
2920 SDL_RenderFillRect(sdl_renderer, &rect);
2923 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2925 static int alpha_direction = 0;
2926 static int alpha_highlight = 0;
2927 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2928 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2930 int grid_xsize = overlay.grid_xsize;
2931 int grid_ysize = overlay.grid_ysize;
2934 if (alpha == alpha_max)
2936 if (alpha_direction < 0)
2938 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2940 if (alpha_highlight == 0)
2941 alpha_direction = 1;
2945 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2947 if (alpha_highlight == alpha_max)
2948 alpha_direction = -1;
2953 alpha_direction = 1;
2954 alpha_highlight = alpha;
2957 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2959 for (x = 0; x < grid_xsize; x++)
2961 for (y = 0; y < grid_ysize; y++)
2963 int grid_button = overlay.grid_button[x][y];
2964 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2965 int alpha_draw = alpha;
2966 int outline_border = MV_NONE;
2967 int border_size = 2;
2968 boolean draw_outlined = setup.touch.draw_outlined;
2969 boolean draw_pressed = setup.touch.draw_pressed;
2971 if (grid_button == CHAR_GRID_BUTTON_NONE)
2974 if (grid_button == overlay.grid_button_highlight)
2976 draw_outlined = FALSE;
2977 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2980 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2983 draw_outlined = FALSE;
2985 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2988 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2990 rect.x = (x + 0) * video.screen_width / grid_xsize;
2991 rect.y = (y + 0) * video.screen_height / grid_ysize;
2992 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2993 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2995 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2997 rect.x += border_size;
2998 rect.w -= border_size;
3000 outline_border |= MV_LEFT;
3003 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
3005 rect.w -= border_size;
3007 outline_border |= MV_RIGHT;
3010 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
3012 rect.y += border_size;
3013 rect.h -= border_size;
3015 outline_border |= MV_UP;
3018 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
3020 rect.h -= border_size;
3022 outline_border |= MV_DOWN;
3027 int rect_x = rect.x +
3028 (outline_border & MV_LEFT ? border_size : 0);
3029 int rect_w = rect.w -
3030 (outline_border & MV_LEFT ? border_size : 0) -
3031 (outline_border & MV_RIGHT ? border_size : 0);
3033 if (outline_border & MV_LEFT)
3034 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
3036 if (outline_border & MV_RIGHT)
3037 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
3038 border_size, rect.h);
3040 if (outline_border & MV_UP)
3041 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
3043 if (outline_border & MV_DOWN)
3044 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
3045 rect_w, border_size);
3049 SDL_RenderFillRect(sdl_renderer, &rect);
3054 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3057 static void DrawTouchInputOverlay(void)
3059 static boolean deactivated = TRUE;
3060 static boolean show_grid = FALSE;
3061 static int alpha = 0;
3062 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3063 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3064 boolean active = (overlay.enabled && overlay.active);
3066 if (!active && deactivated)
3071 if (alpha < alpha_max)
3072 alpha = MIN(alpha + alpha_step, alpha_max);
3074 deactivated = FALSE;
3078 alpha = MAX(0, alpha - alpha_step);
3084 if (overlay.show_grid)
3086 else if (deactivated)
3090 DrawTouchInputOverlay_ShowGrid(alpha);
3092 DrawTouchInputOverlay_ShowGridButtons(alpha);
3095 static void DrawTouchGadgetsOverlay(void)
3097 DrawGadgets_OverlayTouchButtons();