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 (part 1)
66 if (gfx.draw_tile_cursor_function != NULL)
67 gfx.draw_tile_cursor_function(draw_target, TRUE);
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);
77 // copy tile selection cursor to render target buffer, if defined (part 2)
78 if (gfx.draw_tile_cursor_function != NULL)
79 gfx.draw_tile_cursor_function(draw_target, FALSE);
82 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
87 static DelayCounter update_screen_delay = { 50 }; // (milliseconds)
88 SDL_Surface *screen = backbuffer->surface;
90 if (limit_screen_updates &&
91 !DelayReached(&update_screen_delay))
94 LimitScreenUpdates(FALSE);
98 static int LastFrameCounter = 0;
99 boolean changed = (FrameCounter != LastFrameCounter);
101 Debug("internal:frame", "FrameCounter == %d [%s]", FrameCounter,
102 (changed ? "-" : "SAME FRAME UPDATED"));
104 LastFrameCounter = FrameCounter;
107 if (FrameCounter % 2)
113 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
114 gfx.final_screen_bitmap != NULL) // may not be initialized yet
116 // draw global animations using bitmaps instead of using textures
117 // to prevent texture scaling artefacts (this is potentially slower)
119 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
120 gfx.win_xsize, gfx.win_ysize, 0, 0);
122 FinalizeScreen(DRAW_TO_SCREEN);
124 screen = gfx.final_screen_bitmap->surface;
126 // force full window redraw
130 SDL_Texture *sdl_texture = sdl_texture_stream;
132 // deactivate use of target texture if render targets are not supported
133 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
134 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
135 sdl_texture_target == NULL)
136 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
138 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
139 sdl_texture = sdl_texture_target;
143 int bytes_x = screen->pitch / video.width;
144 int bytes_y = screen->pitch;
146 SDL_UpdateTexture(sdl_texture, rect,
147 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
152 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
155 int xoff = video.screen_xoffset;
156 int yoff = video.screen_yoffset;
157 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
158 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
159 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
161 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
162 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
163 dst_rect2 = &dst_rect_screen;
165 dst_rect1 = &dst_rect_screen;
167 #if defined(HAS_SCREEN_KEYBOARD)
168 SDL_Rect src_rect_up = { 0, 0, video.width, video.height };
169 SDL_Rect dst_rect_up = dst_rect_screen;
171 if (video.shifted_up || video.shifted_up_delay.count)
173 int time_current = SDL_GetTicks();
174 int pos = video.shifted_up_pos;
175 int pos_last = video.shifted_up_pos_last;
177 if (!DelayReachedExt(&video.shifted_up_delay, time_current))
179 int delay_count = time_current - video.shifted_up_delay.count;
180 int delay_value = video.shifted_up_delay.value;
182 pos = pos_last + (pos - pos_last) * delay_count / delay_value;
186 video.shifted_up_pos_last = pos;
187 video.shifted_up_delay.count = 0;
191 src_rect_up.h = video.height - pos;
192 dst_rect_up.h = video.height - pos;
194 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
195 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
197 src_rect2 = &src_rect_up;
198 dst_rect2 = &dst_rect_up;
202 src_rect1 = &src_rect_up;
203 dst_rect1 = &dst_rect_up;
208 // clear render target buffer
209 SDL_RenderClear(sdl_renderer);
211 // set renderer to use target texture for rendering
212 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
213 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
214 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
216 // copy backbuffer texture to render target buffer
217 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
218 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
220 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
221 FinalizeScreen(DRAW_TO_SCREEN);
223 // when using target texture, copy it to screen buffer
224 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
225 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
227 SDL_SetRenderTarget(sdl_renderer, NULL);
228 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
231 #if defined(USE_TOUCH_INPUT_OVERLAY)
232 // draw overlay graphics for touch device input, if needed
233 DrawTouchInputOverlay();
235 // draw overlay gadgets for touch device input, if needed
236 DrawTouchGadgetsOverlay();
239 // global synchronization point of the game to align video frame delay
240 if (with_frame_delay)
241 WaitUntilDelayReached(&video.frame_delay);
243 video.frame_counter++;
245 // show render target buffer on screen
246 SDL_RenderPresent(sdl_renderer);
249 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
251 PumpEvents(); // execute event filter actions while waiting
253 UpdateScreenExt(rect, TRUE);
256 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
258 UpdateScreenExt(rect, FALSE);
261 void Delay_WithScreenUpdates(unsigned int delay)
263 unsigned int time_current = SDL_GetTicks();
264 unsigned int time_delayed = time_current + delay;
266 while (time_current < time_delayed)
268 // updating the screen contains waiting for frame delay (non-busy)
269 UpdateScreen_WithFrameDelay(NULL);
271 time_current = SDL_GetTicks();
275 static void SDLSetWindowIcon(char *basename)
277 // (setting the window icon on Mac OS X would replace the high-quality
278 // dock icon with the currently smaller (and uglier) icon from file)
280 #if !defined(PLATFORM_MAC)
281 char *filename = getCustomImageFilename(basename);
282 SDL_Surface *surface;
284 if (filename == NULL)
286 Warn("SDLSetWindowIcon(): cannot find file '%s'", basename);
291 if ((surface = IMG_Load(filename)) == NULL)
293 Warn("IMG_Load('%s') failed: %s", basename, SDL_GetError());
298 // set transparent color
299 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
300 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
302 SDL_SetWindowIcon(sdl_window, surface);
306 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
307 SDL_PixelFormat *format2)
309 return (format1->format == format2->format &&
310 format1->BitsPerPixel == format2->BitsPerPixel &&
311 format1->BytesPerPixel == format2->BytesPerPixel &&
312 format1->Rmask == format2->Rmask &&
313 format1->Gmask == format2->Gmask &&
314 format1->Bmask == format2->Bmask);
317 static void SDLCopyColorKey(SDL_Surface *src_surface, SDL_Surface *dst_surface)
322 // check if source surface has a color key
323 if (SDL_GetColorKey(src_surface, &color_key) == 0)
325 // get RGB values of color key of source surface
326 SDL_GetRGB(color_key, src_surface->format, &r, &g, &b);
328 // get color key from RGB values in destination surface format
329 color_key = SDL_MapRGB(dst_surface->format, r, g, b);
331 // set color key in destination surface
332 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL, color_key);
336 // unset color key in destination surface
337 SDL_SetColorKey(dst_surface, UNSET_TRANSPARENT_PIXEL, 0);
341 static boolean SDLHasColorKey(SDL_Surface *surface)
345 return (SDL_GetColorKey(surface, &color_key) == 0);
348 static boolean SDLHasAlpha(SDL_Surface *surface)
350 SDL_BlendMode blend_mode;
352 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
355 return (blend_mode == SDL_BLENDMODE_BLEND);
358 static void SDLSetSurfaceAlpha(SDL_Surface *surface, boolean set, int alpha)
360 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
362 SDL_SetSurfaceBlendMode(surface, blend_mode);
363 SDL_SetSurfaceAlphaMod(surface, alpha);
366 static void SDLSetTextureAlpha(SDL_Texture *texture, boolean set, int alpha)
368 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
370 SDL_SetTextureBlendMode(texture, blend_mode);
371 SDL_SetTextureAlphaMod(texture, alpha);
374 static void SDLSetBitmapAlpha(Bitmap *bitmap, boolean is_texture,
377 int alpha_next_blit = bitmap->alpha_next_blit;
379 // alpha value must be requested every time before blitting, if needed
380 bitmap->alpha_next_blit = -1;
382 // nothing to do if requested alpha value is already set
383 if (bitmap->alpha[is_texture][is_masked] == alpha_next_blit)
386 // store requested alpha value for masked/unmasked surface/texture
387 bitmap->alpha[is_texture][is_masked] = alpha_next_blit;
389 // set blend mode if bitmap is masked or if alpha value is defined
390 boolean set_blend_mode = (is_masked || alpha_next_blit != -1);
392 // if alpha value is undefined, use default (opaque) alpha value
393 if (alpha_next_blit == -1)
394 alpha_next_blit = SDL_ALPHA_OPAQUE;
397 SDLSetTextureAlpha(is_masked ? bitmap->texture_masked : bitmap->texture,
398 set_blend_mode, alpha_next_blit);
400 SDLSetSurfaceAlpha(is_masked ? bitmap->surface_masked : bitmap->surface,
401 set_blend_mode, alpha_next_blit);
404 void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
406 SDLSetSurfaceAlpha(surface, set, alpha);
409 const char *SDLGetRendererName(void)
411 static SDL_RendererInfo renderer_info;
413 SDL_GetRendererInfo(sdl_renderer, &renderer_info);
415 return renderer_info.name;
418 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
420 SDL_PixelFormat format;
421 SDL_Surface *new_surface;
426 if (backbuffer && backbuffer->surface)
428 format = *backbuffer->surface->format;
429 format.Amask = surface->format->Amask; // keep alpha channel
433 format = *surface->format;
436 new_surface = SDL_ConvertSurface(surface, &format, 0);
438 if (new_surface == NULL)
439 Fail("SDL_ConvertSurface() failed: %s", SDL_GetError());
441 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
442 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
443 SDLCopyColorKey(surface, new_surface);
448 boolean SDLSetNativeSurface(SDL_Surface **surface)
450 SDL_Surface *new_surface;
452 if (surface == NULL ||
454 backbuffer == NULL ||
455 backbuffer->surface == NULL)
458 // if pixel format already optimized for destination surface, do nothing
459 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
462 new_surface = SDLGetNativeSurface(*surface);
464 SDL_FreeSurface(*surface);
466 *surface = new_surface;
471 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
473 if (program.headless)
476 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
479 Fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
484 void SDLCreateBitmapTextures(Bitmap *bitmap)
490 SDL_DestroyTexture(bitmap->texture);
491 if (bitmap->texture_masked)
492 SDL_DestroyTexture(bitmap->texture_masked);
494 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
495 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
498 void SDLFreeBitmapTextures(Bitmap *bitmap)
504 SDL_DestroyTexture(bitmap->texture);
505 if (bitmap->texture_masked)
506 SDL_DestroyTexture(bitmap->texture_masked);
508 bitmap->texture = NULL;
509 bitmap->texture_masked = NULL;
512 void SDLInitVideoDisplay(void)
514 // set hint to select render driver as specified in setup config file
515 if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
516 SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
518 // initialize SDL video
519 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
520 Fail("SDL_InitSubSystem() failed: %s", SDL_GetError());
522 // set default SDL depth
523 video.default_depth = 32; // (how to determine video depth in SDL2?)
525 // Code used with SDL 1.2:
526 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
529 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
531 if (program.headless)
534 video.window_scaling_percent = setup.window_scaling_percent;
535 video.window_scaling_quality = setup.window_scaling_quality;
537 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
539 // SDL 2.0: support for (desktop) fullscreen mode available
540 video.fullscreen_available = TRUE;
542 // open SDL video output device (window or fullscreen mode)
543 if (!SDLSetVideoMode(fullscreen))
544 Fail("setting video mode failed");
546 // !!! SDL2 can only set the window icon if the window already exists !!!
548 SDLSetWindowIcon(program.icon_filename);
550 // set window and icon title
554 static void SDLInitVideoBuffer_DrawBuffer(void)
556 /* SDL cannot directly draw to the visible video framebuffer like X11,
557 but always uses a backbuffer, which is then blitted to the visible
558 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
559 visible video framebuffer with 'SDL_Flip', if the hardware supports
560 this). Therefore do not use an additional backbuffer for drawing, but
561 use a symbolic buffer (distinguishable from the SDL backbuffer) called
562 'window', which indicates that the SDL backbuffer should be updated to
563 the visible video framebuffer when attempting to blit to it.
565 For convenience, it seems to be a good idea to create this symbolic
566 buffer 'window' at the same size as the SDL backbuffer. Although it
567 should never be drawn to directly, it would do no harm nevertheless. */
569 // create additional (symbolic) buffer for double-buffering
570 ReCreateBitmap(&window, video.width, video.height);
572 // create dummy drawing buffer for headless mode, if needed
573 if (program.headless)
574 ReCreateBitmap(&backbuffer, video.width, video.height);
577 void SDLInitVideoBuffer(boolean fullscreen)
579 SDLInitVideoBuffer_VideoBuffer(fullscreen);
580 SDLInitVideoBuffer_DrawBuffer();
583 static boolean SDLCreateScreen(boolean fullscreen)
585 SDL_Surface *new_surface = NULL;
587 int surface_flags_window = SURFACE_FLAGS;
588 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
591 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
593 video.vsync_mode = VSYNC_MODE_OFF;
595 if (!strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF))
597 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
598 video.vsync_mode = VSYNC_MODE_NORMAL;
601 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
602 _without_ enabling 2D/3D acceleration and/or guest additions installed,
603 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
604 it will try to use accelerated graphics and apparently fails miserably) */
605 int renderer_flags = SDL_RENDERER_SOFTWARE;
608 int width = video.width;
609 int height = video.height;
610 int screen_width = video.screen_width;
611 int screen_height = video.screen_height;
612 int surface_flags = (fullscreen ? surface_flags_fullscreen :
613 surface_flags_window);
614 int display_nr = options.display_nr;
616 // default window size is unscaled
617 video.window_width = screen_width;
618 video.window_height = screen_height;
620 // store if initial screen mode is fullscreen mode when changing screen size
621 video.fullscreen_initial = fullscreen;
623 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
625 video.window_width = window_scaling_factor * screen_width;
626 video.window_height = window_scaling_factor * screen_height;
628 if (sdl_texture_stream)
630 SDL_DestroyTexture(sdl_texture_stream);
631 sdl_texture_stream = NULL;
634 if (sdl_texture_target)
636 SDL_DestroyTexture(sdl_texture_target);
637 sdl_texture_target = NULL;
640 if (!(fullscreen && fullscreen_enabled))
644 SDL_DestroyRenderer(sdl_renderer);
650 SDL_SetWindowSize(sdl_window, video.window_width, video.window_height);
654 if (sdl_window == NULL)
655 sdl_window = SDL_CreateWindow(program.window_title,
656 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
657 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
662 if (sdl_window != NULL)
664 if (sdl_renderer == NULL)
665 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
667 if (sdl_renderer != NULL)
669 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
670 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
672 // required for setting adaptive vsync when using OpenGL renderer
673 SDLSetScreenVsyncMode(setup.vsync_mode);
675 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
676 SDL_PIXELFORMAT_ARGB8888,
677 SDL_TEXTUREACCESS_STREAMING,
680 if (SDL_RenderTargetSupported(sdl_renderer))
681 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
682 SDL_PIXELFORMAT_ARGB8888,
683 SDL_TEXTUREACCESS_TARGET,
686 if (sdl_texture_stream != NULL)
688 // use SDL default values for RGB masks and no alpha channel
689 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
691 if (new_surface == NULL)
692 Warn("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
696 Warn("SDL_CreateTexture() failed: %s", SDL_GetError());
701 Warn("SDL_CreateRenderer() failed: %s", SDL_GetError());
706 Warn("SDL_CreateWindow() failed: %s", SDL_GetError());
709 SDLSetScreenProperties();
711 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
712 if (new_surface != NULL)
713 fullscreen_enabled = fullscreen;
715 if (backbuffer == NULL)
716 backbuffer = CreateBitmapStruct();
718 backbuffer->width = video.width;
719 backbuffer->height = video.height;
721 if (backbuffer->surface)
722 SDL_FreeSurface(backbuffer->surface);
724 backbuffer->surface = new_surface;
726 return (new_surface != NULL);
729 boolean SDLSetVideoMode(boolean fullscreen)
731 boolean success = FALSE;
735 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
737 // switch display to fullscreen mode, if available
738 success = SDLCreateScreen(TRUE);
742 // switching display to fullscreen mode failed -- do not try it again
743 video.fullscreen_available = FALSE;
747 video.fullscreen_enabled = TRUE;
751 if ((!fullscreen && video.fullscreen_enabled) || !success)
753 // switch display to window mode
754 success = SDLCreateScreen(FALSE);
758 // switching display to window mode failed -- should not happen
762 video.fullscreen_enabled = FALSE;
763 video.window_scaling_percent = setup.window_scaling_percent;
764 video.window_scaling_quality = setup.window_scaling_quality;
766 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
770 SDLRedrawWindow(); // map window
775 void SDLSetWindowTitle(void)
777 if (sdl_window == NULL)
780 SDL_SetWindowTitle(sdl_window, program.window_title);
783 void SDLSetWindowScaling(int window_scaling_percent)
785 if (sdl_window == NULL)
788 float window_scaling_factor = (float)window_scaling_percent / 100;
789 int new_window_width = (int)(window_scaling_factor * video.screen_width);
790 int new_window_height = (int)(window_scaling_factor * video.screen_height);
792 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
794 video.window_scaling_percent = window_scaling_percent;
795 video.window_width = new_window_width;
796 video.window_height = new_window_height;
801 void SDLSetWindowScalingQuality(char *window_scaling_quality)
803 SDL_Texture *new_texture;
805 if (sdl_texture_stream == NULL)
808 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
810 new_texture = SDL_CreateTexture(sdl_renderer,
811 SDL_PIXELFORMAT_ARGB8888,
812 SDL_TEXTUREACCESS_STREAMING,
813 video.width, video.height);
815 if (new_texture != NULL)
817 SDL_DestroyTexture(sdl_texture_stream);
819 sdl_texture_stream = new_texture;
822 if (SDL_RenderTargetSupported(sdl_renderer))
823 new_texture = SDL_CreateTexture(sdl_renderer,
824 SDL_PIXELFORMAT_ARGB8888,
825 SDL_TEXTUREACCESS_TARGET,
826 video.width, video.height);
830 if (new_texture != NULL)
832 SDL_DestroyTexture(sdl_texture_target);
834 sdl_texture_target = new_texture;
839 video.window_scaling_quality = window_scaling_quality;
842 void SDLSetWindowFullscreen(boolean fullscreen)
844 if (sdl_window == NULL)
847 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
849 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
850 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
852 // if screen size was changed in fullscreen mode, correct desktop window size
853 if (!fullscreen && video.fullscreen_initial)
855 SDLSetWindowScaling(setup.window_scaling_percent);
856 SDL_SetWindowPosition(sdl_window,
857 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr),
858 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr));
860 video.fullscreen_initial = FALSE;
864 void SDLSetDisplaySize(void)
866 if (sdl_renderer != NULL)
870 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
872 video.display_width = w;
873 video.display_height = h;
876 Debug("video", "SDL renderer size: %d x %d",
877 video.display_width, video.display_height);
882 SDL_Rect display_bounds;
884 SDL_GetDisplayBounds(0, &display_bounds);
886 video.display_width = display_bounds.w;
887 video.display_height = display_bounds.h;
890 Debug("video", "SDL display size: %d x %d",
891 video.display_width, video.display_height);
896 void SDLSetScreenSizeAndOffsets(int width, int height)
898 // set default video screen size and offsets
899 video.screen_width = width;
900 video.screen_height = height;
901 video.screen_xoffset = 0;
902 video.screen_yoffset = 0;
904 #if defined(USE_COMPLETE_DISPLAY)
905 float ratio_video = (float) width / height;
906 float ratio_display = (float) video.display_width / video.display_height;
908 if (ratio_video != ratio_display)
910 // adjust drawable screen size to cover the whole device display
912 if (ratio_video < ratio_display)
913 video.screen_width *= ratio_display / ratio_video;
915 video.screen_height *= ratio_video / ratio_display;
917 video.screen_xoffset = (video.screen_width - width) / 2;
918 video.screen_yoffset = (video.screen_height - height) / 2;
921 Debug("video", "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
923 video.screen_width, video.screen_height,
924 ratio_video, ratio_display);
930 void SDLSetScreenSizeForRenderer(int width, int height)
932 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
935 void SDLSetScreenProperties(void)
938 SDLSetScreenSizeAndOffsets(video.width, video.height);
939 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
941 SetOverlayGridSizeAndButtons();
944 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
946 video.screen_rendering_mode =
947 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
948 SPECIAL_RENDERING_BITMAP :
949 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
950 SPECIAL_RENDERING_TARGET:
951 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
952 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
955 void SDLSetScreenVsyncMode(char *vsync_mode)
957 // changing vsync mode without re-creating renderer only supported by OpenGL
958 if (!strPrefixLower((char *)SDLGetRendererName(), "opengl"))
961 int interval = VSYNC_MODE_STR_TO_INT(vsync_mode);
962 int result = SDL_GL_SetSwapInterval(interval);
964 // if adaptive vsync requested, but not supported, retry with normal vsync
965 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
967 interval = VSYNC_MODE_NORMAL;
969 result = SDL_GL_SetSwapInterval(interval);
973 interval = VSYNC_MODE_OFF;
975 video.vsync_mode = interval;
978 void SDLRedrawWindow(void)
980 UpdateScreen_WithoutFrameDelay(NULL);
983 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
986 if (program.headless)
989 SDL_Surface *surface =
990 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
993 Fail("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
995 SDLSetNativeSurface(&surface);
997 bitmap->surface = surface;
1000 void SDLFreeBitmapPointers(Bitmap *bitmap)
1002 if (bitmap->surface)
1003 SDL_FreeSurface(bitmap->surface);
1004 if (bitmap->surface_masked)
1005 SDL_FreeSurface(bitmap->surface_masked);
1007 bitmap->surface = NULL;
1008 bitmap->surface_masked = NULL;
1010 if (bitmap->texture)
1011 SDL_DestroyTexture(bitmap->texture);
1012 if (bitmap->texture_masked)
1013 SDL_DestroyTexture(bitmap->texture_masked);
1015 bitmap->texture = NULL;
1016 bitmap->texture_masked = NULL;
1019 void SDLBlitSurface(SDL_Surface *src_surface, SDL_Surface *dst_surface,
1020 int src_x, int src_y, int width, int height,
1021 int dst_x, int dst_y)
1023 SDL_Rect src_rect, dst_rect;
1028 src_rect.h = height;
1033 dst_rect.h = height;
1035 SDL_BlitSurface(src_surface, &src_rect, dst_surface, &dst_rect);
1038 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1039 int src_x, int src_y, int width, int height,
1040 int dst_x, int dst_y, int mask_mode)
1042 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1043 SDL_Rect src_rect, dst_rect;
1048 src_rect.h = height;
1053 dst_rect.h = height;
1055 SDLSetBitmapAlpha(src_bitmap, FALSE, mask_mode == BLIT_MASKED);
1057 // if (src_bitmap != backbuffer || dst_bitmap != window)
1058 if (!(src_bitmap == backbuffer && dst_bitmap == window))
1059 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
1060 src_bitmap->surface_masked : src_bitmap->surface),
1061 &src_rect, real_dst_bitmap->surface, &dst_rect);
1063 if (dst_bitmap == window)
1064 UpdateScreen_WithFrameDelay(&dst_rect);
1067 void SDLBlitTexture(Bitmap *bitmap,
1068 int src_x, int src_y, int width, int height,
1069 int dst_x, int dst_y, int mask_mode)
1071 SDL_Texture *texture;
1076 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1078 if (texture == NULL)
1084 src_rect.h = height;
1089 dst_rect.h = height;
1091 SDLSetBitmapAlpha(bitmap, TRUE, mask_mode == BLIT_MASKED);
1093 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1096 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1099 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1107 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1109 if (dst_bitmap == window)
1110 UpdateScreen_WithFrameDelay(&rect);
1113 void PrepareFadeBitmap(int draw_target)
1115 Bitmap *fade_bitmap =
1116 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1117 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1119 if (fade_bitmap == NULL)
1122 // copy backbuffer to fading buffer
1123 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1125 // add border and animations to fading buffer
1126 FinalizeScreen(draw_target);
1129 void SDLFadeRectangle(int x, int y, int width, int height,
1130 int fade_mode, int fade_delay, int post_delay,
1131 void (*draw_border_function)(void))
1133 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1134 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1135 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1136 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1137 SDL_Surface *surface_screen = backbuffer->surface;
1138 SDL_Rect src_rect, dst_rect;
1140 int src_x = x, src_y = y;
1141 int dst_x = x, dst_y = y;
1142 unsigned int time_last, time_current;
1144 // store function for drawing global masked border
1145 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1147 // deactivate drawing of global border while fading, if needed
1148 if (draw_border_function == NULL)
1149 gfx.draw_global_border_function = NULL;
1154 src_rect.h = height;
1158 dst_rect.w = width; // (ignored)
1159 dst_rect.h = height; // (ignored)
1161 dst_rect2 = dst_rect;
1163 // before fading in, store backbuffer (without animation graphics)
1164 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1165 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1167 // copy source and target surfaces to temporary surfaces for fading
1168 if (fade_mode & FADE_TYPE_TRANSFORM)
1170 // (source and target fading buffer already prepared)
1172 else if (fade_mode & FADE_TYPE_FADE_IN)
1174 // (target fading buffer already prepared)
1175 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1177 else // FADE_TYPE_FADE_OUT
1179 // (source fading buffer already prepared)
1180 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1183 time_current = SDL_GetTicks();
1185 if (fade_delay <= 0)
1187 // immediately draw final target frame without delay
1188 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1192 // when fading without delay, also skip post delay
1196 if (fade_mode == FADE_MODE_MELT)
1198 boolean done = FALSE;
1199 int melt_pixels = 2;
1200 int melt_columns = width / melt_pixels;
1201 int ypos[melt_columns];
1202 int max_steps = height / 8 + 32;
1207 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1209 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1211 ypos[0] = -GetSimpleRandom(16);
1213 for (i = 1 ; i < melt_columns; i++)
1215 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1217 ypos[i] = ypos[i - 1] + r;
1230 time_last = time_current;
1231 time_current = SDL_GetTicks();
1232 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1233 steps_final = MIN(MAX(0, steps), max_steps);
1237 done = (steps_done >= steps_final);
1239 for (i = 0 ; i < melt_columns; i++)
1247 else if (ypos[i] < height)
1252 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1254 if (ypos[i] + dy >= height)
1255 dy = height - ypos[i];
1257 // copy part of (appearing) target surface to upper area
1258 src_rect.x = src_x + i * melt_pixels;
1259 // src_rect.y = src_y + ypos[i];
1261 src_rect.w = melt_pixels;
1263 src_rect.h = ypos[i] + dy;
1265 dst_rect.x = dst_x + i * melt_pixels;
1266 // dst_rect.y = dst_y + ypos[i];
1269 if (steps_done >= steps_final)
1270 SDL_BlitSurface(surface_target, &src_rect,
1271 surface_screen, &dst_rect);
1275 // copy part of (disappearing) source surface to lower area
1276 src_rect.x = src_x + i * melt_pixels;
1278 src_rect.w = melt_pixels;
1279 src_rect.h = height - ypos[i];
1281 dst_rect.x = dst_x + i * melt_pixels;
1282 dst_rect.y = dst_y + ypos[i];
1284 if (steps_done >= steps_final)
1285 SDL_BlitSurface(surface_source, &src_rect,
1286 surface_screen, &dst_rect);
1292 src_rect.x = src_x + i * melt_pixels;
1294 src_rect.w = melt_pixels;
1295 src_rect.h = height;
1297 dst_rect.x = dst_x + i * melt_pixels;
1300 if (steps_done >= steps_final)
1301 SDL_BlitSurface(surface_target, &src_rect,
1302 surface_screen, &dst_rect);
1306 if (steps_done >= steps_final)
1308 if (draw_border_function != NULL)
1309 draw_border_function();
1311 UpdateScreen_WithFrameDelay(&dst_rect2);
1313 if (PendingEscapeKeyEvent())
1318 else if (fade_mode == FADE_MODE_CURTAIN)
1322 int xx_size = width / 2;
1324 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1326 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1328 for (xx = 0; xx < xx_size;)
1330 time_last = time_current;
1331 time_current = SDL_GetTicks();
1332 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1333 xx_final = MIN(MAX(0, xx), xx_size);
1338 src_rect.h = height;
1343 // draw new (target) image to screen buffer
1344 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1346 if (xx_final < xx_size)
1348 src_rect.w = xx_size - xx_final;
1349 src_rect.h = height;
1351 // draw old (source) image to screen buffer (left side)
1353 src_rect.x = src_x + xx_final;
1356 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1358 // draw old (source) image to screen buffer (right side)
1360 src_rect.x = src_x + xx_size;
1361 dst_rect.x = dst_x + xx_size + xx_final;
1363 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1366 if (draw_border_function != NULL)
1367 draw_border_function();
1369 // only update the region of the screen that is affected from fading
1370 UpdateScreen_WithFrameDelay(&dst_rect2);
1372 if (PendingEscapeKeyEvent())
1376 else // fading in, fading out or cross-fading
1381 for (alpha = 0.0; alpha < 255.0;)
1383 time_last = time_current;
1384 time_current = SDL_GetTicks();
1385 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1386 alpha_final = MIN(MAX(0, alpha), 255);
1388 // draw existing (source) image to screen buffer
1389 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1391 // draw new (target) image to screen buffer using alpha blending
1392 SDLSetAlpha(surface_target, TRUE, alpha_final);
1393 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1395 if (draw_border_function != NULL)
1396 draw_border_function();
1398 // only update the region of the screen that is affected from fading
1399 UpdateScreen_WithFrameDelay(&dst_rect);
1401 if (PendingEscapeKeyEvent())
1407 Delay_WithScreenUpdates(post_delay);
1409 // restore function for drawing global masked border
1410 gfx.draw_global_border_function = draw_global_border_function;
1412 // after fading in, restore backbuffer (without animation graphics)
1413 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1414 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1417 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1418 int to_x, int to_y, Uint32 color)
1420 SDL_Surface *surface = dst_bitmap->surface;
1424 swap_numbers(&from_x, &to_x);
1427 swap_numbers(&from_y, &to_y);
1431 rect.w = (to_x - from_x + 1);
1432 rect.h = (to_y - from_y + 1);
1434 SDL_FillRect(surface, &rect, color);
1437 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1438 int to_x, int to_y, Uint32 color)
1440 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1443 #if ENABLE_UNUSED_CODE
1444 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1445 int num_points, Uint32 color)
1450 for (i = 0; i < num_points - 1; i++)
1452 for (x = 0; x < line_width; x++)
1454 for (y = 0; y < line_width; y++)
1456 int dx = x - line_width / 2;
1457 int dy = y - line_width / 2;
1459 if ((x == 0 && y == 0) ||
1460 (x == 0 && y == line_width - 1) ||
1461 (x == line_width - 1 && y == 0) ||
1462 (x == line_width - 1 && y == line_width - 1))
1465 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1466 points[i+1].x + dx, points[i+1].y + dy, color);
1473 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1475 SDL_Surface *surface = src_bitmap->surface;
1477 switch (surface->format->BytesPerPixel)
1479 case 1: // assuming 8-bpp
1481 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1485 case 2: // probably 15-bpp or 16-bpp
1487 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1491 case 3: // slow 24-bpp mode; usually not used
1494 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1498 shift = surface->format->Rshift;
1499 color |= *(pix + shift / 8) >> shift;
1500 shift = surface->format->Gshift;
1501 color |= *(pix + shift / 8) >> shift;
1502 shift = surface->format->Bshift;
1503 color |= *(pix + shift / 8) >> shift;
1509 case 4: // probably 32-bpp
1511 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1520 // ============================================================================
1521 // The following functions were taken from the SGE library
1522 // (SDL Graphics Extension Library) by Anders Lindström
1523 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1524 // ============================================================================
1526 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1528 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1530 switch (surface->format->BytesPerPixel)
1535 *((Uint8 *)surface->pixels + y * surface->pitch + x) = color;
1541 // Probably 15-bpp or 16-bpp
1542 *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x) = color;
1548 // Slow 24-bpp mode, usually not used
1552 // Gack - slow, but endian correct
1553 pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1554 shift = surface->format->Rshift;
1555 *(pix + shift / 8) = color>>shift;
1556 shift = surface->format->Gshift;
1557 *(pix + shift / 8) = color>>shift;
1558 shift = surface->format->Bshift;
1559 *(pix + shift / 8) = color>>shift;
1566 *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x) = color;
1574 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1575 Uint8 R, Uint8 G, Uint8 B)
1577 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1580 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1582 *((Uint8 *)surface->pixels + y * surface->pitch + x) = color;
1585 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1587 *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x) = color;
1590 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1595 // Gack - slow, but endian correct
1596 pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1597 shift = surface->format->Rshift;
1598 *(pix + shift / 8) = color>>shift;
1599 shift = surface->format->Gshift;
1600 *(pix + shift / 8) = color>>shift;
1601 shift = surface->format->Bshift;
1602 *(pix + shift / 8) = color>>shift;
1605 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1607 *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x) = color;
1610 static void _PutPixelX(SDL_Surface *dest, Sint16 x, Sint16 y, Uint32 color)
1612 switch (dest->format->BytesPerPixel)
1615 *((Uint8 *)dest->pixels + y * dest->pitch + x) = color;
1619 *((Uint16 *)dest->pixels + y * dest->pitch / 2 + x) = color;
1623 _PutPixel24(dest, x, y, color);
1627 *((Uint32 *)dest->pixels + y * dest->pitch / 4 + x) = color;
1633 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1635 if (SDL_MUSTLOCK(surface))
1637 if (SDL_LockSurface(surface) < 0)
1643 _PutPixel(surface, x, y, color);
1645 if (SDL_MUSTLOCK(surface))
1647 SDL_UnlockSurface(surface);
1652 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1653 Uint8 r, Uint8 g, Uint8 b)
1655 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1658 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1660 if (y >= 0 && y <= dest->h - 1)
1662 switch (dest->format->BytesPerPixel)
1665 return y * dest->pitch;
1669 return y * dest->pitch / 2;
1673 return y * dest->pitch;
1677 return y * dest->pitch / 4;
1685 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1688 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1690 switch (surface->format->BytesPerPixel)
1695 *((Uint8 *)surface->pixels + ypitch + x) = color;
1701 // Probably 15-bpp or 16-bpp
1702 *((Uint16 *)surface->pixels + ypitch + x) = color;
1708 // Slow 24-bpp mode, usually not used
1712 // Gack - slow, but endian correct
1713 pix = (Uint8 *)surface->pixels + ypitch + x * 3;
1714 shift = surface->format->Rshift;
1715 *(pix + shift / 8) = color>>shift;
1716 shift = surface->format->Gshift;
1717 *(pix + shift / 8) = color>>shift;
1718 shift = surface->format->Bshift;
1719 *(pix + shift / 8) = color>>shift;
1726 *((Uint32 *)surface->pixels + ypitch + x) = color;
1733 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1738 if (SDL_MUSTLOCK(Surface))
1740 if (SDL_LockSurface(Surface) < 0)
1754 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1758 if (x2 > Surface->w - 1)
1759 x2 = Surface->w - 1;
1766 SDL_FillRect(Surface, &l, Color);
1768 if (SDL_MUSTLOCK(Surface))
1770 SDL_UnlockSurface(Surface);
1774 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1775 Uint8 R, Uint8 G, Uint8 B)
1777 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1780 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1793 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1797 if (x2 > Surface->w - 1)
1798 x2 = Surface->w - 1;
1805 SDL_FillRect(Surface, &l, Color);
1808 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1813 if (SDL_MUSTLOCK(Surface))
1815 if (SDL_LockSurface(Surface) < 0)
1829 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1833 if (y2 > Surface->h - 1)
1834 y2 = Surface->h - 1;
1841 SDL_FillRect(Surface, &l, Color);
1843 if (SDL_MUSTLOCK(Surface))
1845 SDL_UnlockSurface(Surface);
1849 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1850 Uint8 R, Uint8 G, Uint8 B)
1852 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1855 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1868 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1872 if (y2 > Surface->h - 1)
1873 y2 = Surface->h - 1;
1880 SDL_FillRect(Surface, &l, Color);
1884 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1885 Sint16 x2, Sint16 y2, Uint32 Color,
1886 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1889 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1894 sdx = (dx < 0) ? -1 : 1;
1895 sdy = (dy < 0) ? -1 : 1;
1907 for (x = 0; x < dx; x++)
1909 Callback(Surface, px, py, Color);
1923 for (y = 0; y < dy; y++)
1925 Callback(Surface, px, py, Color);
1940 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1941 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1942 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1945 sge_DoLine(Surface, X1, Y1, X2, Y2,
1946 SDL_MapRGB(Surface->format, R, G, B), Callback);
1950 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1953 if (SDL_MUSTLOCK(Surface))
1955 if (SDL_LockSurface(Surface) < 0)
1960 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1962 // unlock the display
1963 if (SDL_MUSTLOCK(Surface))
1965 SDL_UnlockSurface(Surface);
1970 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1971 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1973 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1977 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1979 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1983 // ----------------------------------------------------------------------------
1984 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1985 // ----------------------------------------------------------------------------
1987 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1988 int src_x, int src_y, int width, int height,
1989 int dst_x, int dst_y)
1993 for (y = 0; y < height; y++)
1995 for (x = 0; x < width; x++)
1997 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1999 if (pixel != BLACK_PIXEL)
2000 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
2006 // ============================================================================
2007 // The following functions were taken from the SDL_gfx library version 2.0.3
2008 // (Rotozoomer) by Andreas Schiffler
2009 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
2010 // ============================================================================
2012 // ----------------------------------------------------------------------------
2015 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
2016 // ----------------------------------------------------------------------------
2026 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
2029 tColorRGBA *sp, *csp, *dp;
2033 sp = csp = (tColorRGBA *) src->pixels;
2034 dp = (tColorRGBA *) dst->pixels;
2035 dgap = dst->pitch - dst->w * 4;
2037 for (y = 0; y < dst->h; y++)
2041 for (x = 0; x < dst->w; x++)
2043 tColorRGBA *sp0 = sp;
2044 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
2045 tColorRGBA *sp00 = &sp0[0];
2046 tColorRGBA *sp01 = &sp0[1];
2047 tColorRGBA *sp10 = &sp1[0];
2048 tColorRGBA *sp11 = &sp1[1];
2051 // create new color pixel from all four source color pixels
2052 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
2053 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
2054 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
2055 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
2060 // advance source pointers
2063 // advance destination pointer
2067 // advance source pointer
2068 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2070 // advance destination pointers
2071 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2077 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2079 int x, y, *sax, *say, *csax, *csay;
2081 tColorRGBA *sp, *csp, *csp0, *dp;
2084 // use specialized zoom function when scaling down to exactly half size
2085 if (src->w == 2 * dst->w &&
2086 src->h == 2 * dst->h)
2087 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2090 sx = (float) src->w / (float) dst->w;
2091 sy = (float) src->h / (float) dst->h;
2093 // allocate memory for row increments
2094 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2095 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2097 // precalculate row increments
2098 for (x = 0; x <= dst->w; x++)
2099 *csax++ = (int)(sx * x);
2101 for (y = 0; y <= dst->h; y++)
2102 *csay++ = (int)(sy * y);
2105 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2106 dp = (tColorRGBA *) dst->pixels;
2107 dgap = dst->pitch - dst->w * 4;
2110 for (y = 0; y < dst->h; y++)
2115 for (x = 0; x < dst->w; x++)
2120 // advance source pointers
2124 // advance destination pointer
2128 // advance source pointer
2130 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2132 // advance destination pointers
2133 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2142 // ----------------------------------------------------------------------------
2145 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2146 // ----------------------------------------------------------------------------
2148 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2150 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2151 Uint8 *sp, *dp, *csp;
2155 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2156 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2158 // allocate memory for row increments
2159 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2160 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2162 // precalculate row increments
2165 for (x = 0; x < dst->w; x++)
2168 *csax = (csx >> 16);
2175 for (y = 0; y < dst->h; y++)
2178 *csay = (csy >> 16);
2185 for (x = 0; x < dst->w; x++)
2193 for (y = 0; y < dst->h; y++)
2200 sp = csp = (Uint8 *) src->pixels;
2201 dp = (Uint8 *) dst->pixels;
2202 dgap = dst->pitch - dst->w;
2206 for (y = 0; y < dst->h; y++)
2210 for (x = 0; x < dst->w; x++)
2215 // advance source pointers
2219 // advance destination pointer
2223 // advance source pointer (for row)
2224 csp += ((*csay) * src->pitch);
2227 // advance destination pointers
2237 // ----------------------------------------------------------------------------
2240 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2241 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2242 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2243 // into a 32bit RGBA format on the fly.
2244 // ----------------------------------------------------------------------------
2246 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2248 SDL_Surface *zoom_src = NULL;
2249 SDL_Surface *zoom_dst = NULL;
2250 boolean is_converted = FALSE;
2257 // determine if source surface is 32 bit or 8 bit
2258 is_32bit = (src->format->BitsPerPixel == 32);
2260 if (is_32bit || src->format->BitsPerPixel == 8)
2262 // use source surface 'as is'
2267 // new source surface is 32 bit with a defined RGB ordering
2268 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2269 0x000000ff, 0x0000ff00, 0x00ff0000,
2270 (src->format->Amask ? 0xff000000 : 0));
2271 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2273 is_converted = TRUE;
2276 // allocate surface to completely contain the zoomed surface
2279 // target surface is 32 bit with source RGBA/ABGR ordering
2280 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2281 zoom_src->format->Rmask,
2282 zoom_src->format->Gmask,
2283 zoom_src->format->Bmask,
2284 zoom_src->format->Amask);
2288 // target surface is 8 bit
2289 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2293 // lock source surface
2294 SDL_LockSurface(zoom_src);
2296 // check which kind of surface we have
2299 // call the 32 bit transformation routine to do the zooming
2300 zoomSurfaceRGBA(zoom_src, zoom_dst);
2305 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2306 zoom_dst->format->palette->colors[i] =
2307 zoom_src->format->palette->colors[i];
2308 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2310 // call the 8 bit transformation routine to do the zooming
2311 zoomSurfaceY(zoom_src, zoom_dst);
2314 // unlock source surface
2315 SDL_UnlockSurface(zoom_src);
2317 // free temporary surface
2319 SDL_FreeSurface(zoom_src);
2321 // return destination surface
2325 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2327 SDL_Surface *new_surface;
2329 if (surface == NULL)
2332 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2333 Fail("SDLGetNativeSurface() failed");
2335 // remove alpha channel from native non-transparent surface, if defined
2336 SDLSetAlpha(new_surface, FALSE, 0);
2338 // remove transparent color from native non-transparent surface, if defined
2339 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2344 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2346 Bitmap *dst_bitmap = CreateBitmapStruct();
2347 SDL_Surface *src_surface = src_bitmap->surface_masked;
2348 SDL_Surface *dst_surface;
2350 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2351 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2353 dst_bitmap->width = dst_width;
2354 dst_bitmap->height = dst_height;
2356 // create zoomed temporary surface from source surface
2357 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2359 // create native format destination surface from zoomed temporary surface
2360 SDLSetNativeSurface(&dst_surface);
2362 // set color key for zoomed surface from source surface, if defined
2363 if (SDLHasColorKey(src_surface))
2364 SDLCopyColorKey(src_surface, dst_surface);
2366 // create native non-transparent surface for opaque blitting
2367 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2369 // set native transparent surface for masked blitting
2370 dst_bitmap->surface_masked = dst_surface;
2376 // ============================================================================
2377 // load image to bitmap
2378 // ============================================================================
2380 Bitmap *SDLLoadImage(char *filename)
2382 Bitmap *new_bitmap = CreateBitmapStruct();
2383 SDL_Surface *sdl_image_tmp;
2385 if (program.headless)
2387 // prevent sanity check warnings at later stage
2388 new_bitmap->width = new_bitmap->height = 1;
2393 print_timestamp_init("SDLLoadImage");
2395 print_timestamp_time(getBaseNamePtr(filename));
2397 // load image to temporary surface
2398 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2399 Fail("IMG_Load('%s') failed: %s", getBaseNamePtr(filename), SDL_GetError());
2401 print_timestamp_time("IMG_Load");
2403 UPDATE_BUSY_STATE();
2405 // create native non-transparent surface for current image
2406 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2407 Fail("SDLGetOpaqueSurface() failed");
2409 print_timestamp_time("SDLGetNativeSurface (opaque)");
2411 UPDATE_BUSY_STATE();
2413 // set black pixel to transparent if no alpha channel / transparent color
2414 if (!SDLHasAlpha(sdl_image_tmp) &&
2415 !SDLHasColorKey(sdl_image_tmp))
2416 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2417 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2419 // create native transparent surface for current image
2420 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2421 Fail("SDLGetNativeSurface() failed");
2423 print_timestamp_time("SDLGetNativeSurface (masked)");
2425 UPDATE_BUSY_STATE();
2427 // free temporary surface
2428 SDL_FreeSurface(sdl_image_tmp);
2430 new_bitmap->width = new_bitmap->surface->w;
2431 new_bitmap->height = new_bitmap->surface->h;
2433 print_timestamp_done("SDLLoadImage");
2439 // ----------------------------------------------------------------------------
2440 // custom cursor fuctions
2441 // ----------------------------------------------------------------------------
2443 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2445 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2446 cursor_info->width, cursor_info->height,
2447 cursor_info->hot_x, cursor_info->hot_y);
2450 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2452 static struct MouseCursorInfo *last_cursor_info = NULL;
2453 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2454 static SDL_Cursor *cursor_default = NULL;
2455 static SDL_Cursor *cursor_current = NULL;
2457 // if invoked for the first time, store the SDL default cursor
2458 if (cursor_default == NULL)
2459 cursor_default = SDL_GetCursor();
2461 // only create new cursor if cursor info (custom only) has changed
2462 if (cursor_info != NULL && cursor_info != last_cursor_info)
2464 cursor_current = create_cursor(cursor_info);
2465 last_cursor_info = cursor_info;
2468 // only set new cursor if cursor info (custom or NULL) has changed
2469 if (cursor_info != last_cursor_info2)
2470 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2472 last_cursor_info2 = cursor_info;
2476 // ============================================================================
2478 // ============================================================================
2480 void SDLOpenAudio(void)
2482 if (program.headless)
2485 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2487 Warn("SDL_InitSubSystem() failed: %s", SDL_GetError());
2492 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2493 AUDIO_NUM_CHANNELS_STEREO,
2494 setup.system.audio_fragment_size) < 0)
2496 Warn("Mix_OpenAudio() failed: %s", SDL_GetError());
2501 audio.sound_available = TRUE;
2502 audio.music_available = TRUE;
2503 audio.loops_available = TRUE;
2504 audio.sound_enabled = TRUE;
2506 // set number of available mixer channels
2507 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2508 audio.music_channel = MUSIC_CHANNEL;
2509 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2511 Mixer_InitChannels();
2514 void SDLCloseAudio(void)
2517 Mix_HaltChannel(-1);
2520 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2524 // ============================================================================
2526 // ============================================================================
2528 void SDLWaitEvent(Event *event)
2530 SDL_WaitEvent(event);
2533 void SDLCorrectRawMousePosition(int *x, int *y)
2535 if (sdl_renderer == NULL)
2538 // this corrects the raw mouse position for logical screen size within event
2539 // filters (correction done later by SDL library when handling mouse events)
2542 float scale_x, scale_y;
2544 SDL_RenderGetViewport(sdl_renderer, &viewport);
2545 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2547 *x = (int)(*x / scale_x);
2548 *y = (int)(*y / scale_y);
2555 // ============================================================================
2556 // joystick functions
2557 // ============================================================================
2559 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2560 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2561 static int sdl_js_axis[MAX_PLAYERS][2];
2562 static int sdl_js_button[MAX_PLAYERS][2];
2563 static boolean sdl_is_controller[MAX_PLAYERS];
2565 void SDLClearJoystickState(void)
2569 for (i = 0; i < MAX_PLAYERS; i++)
2571 for (j = 0; j < 2; j++)
2573 sdl_js_axis_raw[i][j] = -1;
2574 sdl_js_axis[i][j] = 0;
2575 sdl_js_button[i][j] = 0;
2580 boolean SDLOpenJoystick(int nr)
2582 if (nr < 0 || nr >= MAX_PLAYERS)
2585 sdl_is_controller[nr] = SDL_IsGameController(nr);
2588 Debug("joystick", "opening joystick %d (%s)",
2589 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2592 if (sdl_is_controller[nr])
2593 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2595 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2597 return (sdl_joystick[nr] != NULL);
2600 void SDLCloseJoystick(int nr)
2602 if (nr < 0 || nr >= MAX_PLAYERS)
2606 Debug("joystick", "closing joystick %d", nr);
2609 if (sdl_is_controller[nr])
2610 SDL_GameControllerClose(sdl_joystick[nr]);
2612 SDL_JoystickClose(sdl_joystick[nr]);
2614 sdl_joystick[nr] = NULL;
2617 boolean SDLCheckJoystickOpened(int nr)
2619 if (nr < 0 || nr >= MAX_PLAYERS)
2622 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2625 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2627 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2628 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2629 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2630 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2632 if (nr < 0 || nr >= MAX_PLAYERS)
2638 // prevent (slightly jittering, but centered) axis A from resetting axis B
2639 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2640 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2643 sdl_js_axis[nr][axis_id] = axis_value;
2644 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2647 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2649 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2650 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2651 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2652 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2653 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2654 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2655 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2656 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2659 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2660 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2661 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2662 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2663 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2664 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2665 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2666 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2668 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2669 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2670 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2671 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2672 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2674 if (nr < 0 || nr >= MAX_PLAYERS)
2677 if (button_id == -1)
2680 sdl_js_button[nr][button_id] = button_state;
2683 void HandleJoystickEvent(Event *event)
2685 // when using joystick, disable overlay touch buttons
2686 runtime.uses_touch_device = FALSE;
2688 switch (event->type)
2690 case SDL_CONTROLLERDEVICEADDED:
2692 Debug("joystick", "SDL_CONTROLLERDEVICEADDED: device %d added",
2693 event->cdevice.which);
2698 case SDL_CONTROLLERDEVICEREMOVED:
2700 Debug("joystick", "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2701 event->cdevice.which);
2706 case SDL_CONTROLLERAXISMOTION:
2708 Debug("joystick", "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2709 event->caxis.which, event->caxis.axis, event->caxis.value);
2711 setJoystickAxis(event->caxis.which,
2713 event->caxis.value);
2716 case SDL_CONTROLLERBUTTONDOWN:
2718 Debug("joystick", "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2719 event->cbutton.which, event->cbutton.button);
2721 setJoystickButton(event->cbutton.which,
2722 event->cbutton.button,
2726 case SDL_CONTROLLERBUTTONUP:
2728 Debug("joystick", "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2729 event->cbutton.which, event->cbutton.button);
2731 setJoystickButton(event->cbutton.which,
2732 event->cbutton.button,
2736 case SDL_JOYAXISMOTION:
2737 if (sdl_is_controller[event->jaxis.which])
2741 Debug("joystick", "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2742 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2744 if (event->jaxis.axis < 4)
2745 setJoystickAxis(event->jaxis.which,
2747 event->jaxis.value);
2750 case SDL_JOYBUTTONDOWN:
2751 if (sdl_is_controller[event->jaxis.which])
2755 Debug("joystick", "SDL_JOYBUTTONDOWN: device %d, button %d",
2756 event->jbutton.which, event->jbutton.button);
2758 if (event->jbutton.button < 4)
2759 setJoystickButton(event->jbutton.which,
2760 event->jbutton.button,
2764 case SDL_JOYBUTTONUP:
2765 if (sdl_is_controller[event->jaxis.which])
2769 Debug("joystick", "SDL_JOYBUTTONUP: device %d, button %d",
2770 event->jbutton.which, event->jbutton.button);
2772 if (event->jbutton.button < 4)
2773 setJoystickButton(event->jbutton.which,
2774 event->jbutton.button,
2783 void SDLInitJoysticks(void)
2785 static boolean sdl_joystick_subsystem_initialized = FALSE;
2786 boolean print_warning = !sdl_joystick_subsystem_initialized;
2787 char *mappings_file_base = getPath2(options.conf_directory,
2788 GAMECONTROLLER_BASENAME);
2789 char *mappings_file_user = getPath2(getMainUserGameDataDir(),
2790 GAMECONTROLLER_BASENAME);
2794 if (!sdl_joystick_subsystem_initialized)
2796 sdl_joystick_subsystem_initialized = TRUE;
2798 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2800 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2801 Fail("SDL_Init() failed: %s", SDL_GetError());
2803 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2805 // the included game controller base mappings should always be found
2806 if (num_mappings == -1)
2807 Warn("no game controller base mappings found");
2810 Debug("joystick", "%d game controller base mapping(s) added",
2814 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2817 // the personal game controller user mappings may or may not be found
2818 if (num_mappings == -1)
2819 Warn("no game controller user mappings found");
2821 Debug("joystick", , "%d game controller user mapping(s) added",
2824 Debug("joystick", "%d joystick(s) found:", SDL_NumJoysticks());
2827 checked_free(mappings_file_base);
2828 checked_free(mappings_file_user);
2831 for (i = 0; i < SDL_NumJoysticks(); i++)
2833 const char *name, *type;
2835 if (SDL_IsGameController(i))
2837 name = SDL_GameControllerNameForIndex(i);
2838 type = "game controller";
2842 name = SDL_JoystickNameForIndex(i);
2846 Debug("joystick", "- joystick %d (%s): '%s'",
2847 i, type, (name ? name : "(Unknown)"));
2852 // assign joysticks from configured to connected joystick for all players
2853 for (i = 0; i < MAX_PLAYERS; i++)
2855 // get configured joystick for this player
2856 char *device_name = setup.input[i].joy.device_name;
2857 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2859 if (joystick_nr >= SDL_NumJoysticks())
2861 if (setup.input[i].use_joystick && print_warning)
2862 Warn("cannot find joystick %d", joystick_nr);
2867 // store configured joystick number for each player
2868 joystick.nr[i] = joystick_nr;
2871 // now open all connected joysticks (regardless if configured or not)
2872 for (i = 0; i < SDL_NumJoysticks(); i++)
2874 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2875 if (SDLCheckJoystickOpened(i))
2876 SDLCloseJoystick(i);
2878 if (SDLOpenJoystick(i))
2879 joystick.status = JOYSTICK_ACTIVATED;
2880 else if (print_warning)
2881 Warn("cannot open joystick %d", i);
2884 SDLClearJoystickState();
2887 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2889 if (nr < 0 || nr >= MAX_PLAYERS)
2893 *x = sdl_js_axis[nr][0];
2895 *y = sdl_js_axis[nr][1];
2898 *b1 = sdl_js_button[nr][0];
2900 *b2 = sdl_js_button[nr][1];
2906 // ============================================================================
2907 // touch input overlay functions
2908 // ============================================================================
2910 #if defined(USE_TOUCH_INPUT_OVERLAY)
2911 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2914 int grid_xsize = overlay.grid_xsize;
2915 int grid_ysize = overlay.grid_ysize;
2918 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2919 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2921 for (x = 0; x < grid_xsize; x++)
2923 rect.x = (x + 0) * video.screen_width / grid_xsize;
2924 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2926 for (y = 0; y < grid_ysize; y++)
2928 rect.y = (y + 0) * video.screen_height / grid_ysize;
2929 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2931 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2932 SDL_RenderDrawRect(sdl_renderer, &rect);
2936 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2939 static void RenderFillRectangle(int x, int y, int width, int height)
2941 SDL_Rect rect = { x, y, width, height };
2943 SDL_RenderFillRect(sdl_renderer, &rect);
2946 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2948 static int alpha_direction = 0;
2949 static int alpha_highlight = 0;
2950 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2951 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2953 int grid_xsize = overlay.grid_xsize;
2954 int grid_ysize = overlay.grid_ysize;
2957 if (alpha == alpha_max)
2959 if (alpha_direction < 0)
2961 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2963 if (alpha_highlight == 0)
2964 alpha_direction = 1;
2968 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2970 if (alpha_highlight == alpha_max)
2971 alpha_direction = -1;
2976 alpha_direction = 1;
2977 alpha_highlight = alpha;
2980 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2982 for (x = 0; x < grid_xsize; x++)
2984 for (y = 0; y < grid_ysize; y++)
2986 int grid_button = overlay.grid_button[x][y];
2987 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2988 int alpha_draw = alpha;
2989 int outline_border = MV_NONE;
2990 int border_size = 2;
2991 boolean draw_outlined = setup.touch.draw_outlined;
2992 boolean draw_pressed = setup.touch.draw_pressed;
2994 if (grid_button == CHAR_GRID_BUTTON_NONE)
2997 if (grid_button == overlay.grid_button_highlight)
2999 draw_outlined = FALSE;
3000 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
3003 if (draw_pressed && overlay.grid_button_action & grid_button_action)
3006 draw_outlined = FALSE;
3008 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
3011 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
3013 rect.x = (x + 0) * video.screen_width / grid_xsize;
3014 rect.y = (y + 0) * video.screen_height / grid_ysize;
3015 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
3016 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
3018 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
3020 rect.x += border_size;
3021 rect.w -= border_size;
3023 outline_border |= MV_LEFT;
3026 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
3028 rect.w -= border_size;
3030 outline_border |= MV_RIGHT;
3033 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
3035 rect.y += border_size;
3036 rect.h -= border_size;
3038 outline_border |= MV_UP;
3041 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
3043 rect.h -= border_size;
3045 outline_border |= MV_DOWN;
3050 int rect_x = rect.x +
3051 (outline_border & MV_LEFT ? border_size : 0);
3052 int rect_w = rect.w -
3053 (outline_border & MV_LEFT ? border_size : 0) -
3054 (outline_border & MV_RIGHT ? border_size : 0);
3056 if (outline_border & MV_LEFT)
3057 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
3059 if (outline_border & MV_RIGHT)
3060 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
3061 border_size, rect.h);
3063 if (outline_border & MV_UP)
3064 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
3066 if (outline_border & MV_DOWN)
3067 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
3068 rect_w, border_size);
3072 SDL_RenderFillRect(sdl_renderer, &rect);
3077 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3080 static void DrawTouchInputOverlay(void)
3082 static boolean deactivated = TRUE;
3083 static boolean show_grid = FALSE;
3084 static int alpha = 0;
3085 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3086 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3087 boolean active = (overlay.enabled && overlay.active);
3089 if (!active && deactivated)
3094 if (alpha < alpha_max)
3095 alpha = MIN(alpha + alpha_step, alpha_max);
3097 deactivated = FALSE;
3101 alpha = MAX(0, alpha - alpha_step);
3107 if (overlay.show_grid)
3109 else if (deactivated)
3113 DrawTouchInputOverlay_ShowGrid(alpha);
3115 DrawTouchInputOverlay_ShowGridButtons(alpha);
3118 static void DrawTouchGadgetsOverlay(void)
3120 DrawGadgets_OverlayTouchButtons();