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 tile selection cursor to render target buffer, if defined (part 2)
74 if (gfx.draw_tile_cursor_function != NULL)
75 gfx.draw_tile_cursor_function(draw_target, FALSE);
77 // copy global animations to render target buffer, if defined (mouse pointer)
78 if (gfx.draw_global_anim_function != NULL)
79 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_3);
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 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
420 SDL_Surface *new_surface;
425 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
426 Fail("SDLGetNativeSurface() failed");
428 // remove alpha channel from native non-transparent surface, if defined
429 SDLSetAlpha(new_surface, FALSE, 0);
431 // remove transparent color from native non-transparent surface, if defined
432 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
437 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
439 SDL_PixelFormat format;
440 SDL_Surface *new_surface;
445 if (backbuffer && backbuffer->surface)
447 format = *backbuffer->surface->format;
448 format.Amask = surface->format->Amask; // keep alpha channel
452 format = *surface->format;
455 new_surface = SDL_ConvertSurface(surface, &format, 0);
457 if (new_surface == NULL)
458 Fail("SDL_ConvertSurface() failed: %s", SDL_GetError());
460 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
461 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
462 SDLCopyColorKey(surface, new_surface);
467 boolean SDLSetNativeSurface(SDL_Surface **surface)
469 SDL_Surface *new_surface;
471 if (surface == NULL ||
473 backbuffer == NULL ||
474 backbuffer->surface == NULL)
477 // if pixel format already optimized for destination surface, do nothing
478 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
481 new_surface = SDLGetNativeSurface(*surface);
483 SDL_FreeSurface(*surface);
485 *surface = new_surface;
490 SDL_Surface *SDLCreateNativeSurface(int width, int height, int depth)
492 if (program.headless)
495 SDL_Surface *surface = SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
498 Fail("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
500 SDLSetNativeSurface(&surface);
505 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
507 if (program.headless)
510 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
513 Fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
518 void SDLCreateBitmapTextures(Bitmap *bitmap)
524 SDL_DestroyTexture(bitmap->texture);
525 if (bitmap->texture_masked)
526 SDL_DestroyTexture(bitmap->texture_masked);
528 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
529 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
532 void SDLFreeBitmapTextures(Bitmap *bitmap)
538 SDL_DestroyTexture(bitmap->texture);
539 if (bitmap->texture_masked)
540 SDL_DestroyTexture(bitmap->texture_masked);
542 bitmap->texture = NULL;
543 bitmap->texture_masked = NULL;
546 void SDLInitVideoDisplay(void)
548 // set hint to select render driver as specified in setup config file
549 if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
550 SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
552 // initialize SDL video
553 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
554 Fail("SDL_InitSubSystem() failed: %s", SDL_GetError());
556 // set default SDL depth
557 video.default_depth = 32; // (how to determine video depth in SDL2?)
559 // Code used with SDL 1.2:
560 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
563 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
565 if (program.headless)
568 video.window_scaling_percent = setup.window_scaling_percent;
569 video.window_scaling_quality = setup.window_scaling_quality;
571 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
573 // SDL 2.0: support for (desktop) fullscreen mode available
574 video.fullscreen_available = TRUE;
576 // open SDL video output device (window or fullscreen mode)
577 if (!SDLSetVideoMode(fullscreen))
578 Fail("setting video mode failed");
580 // !!! SDL2 can only set the window icon if the window already exists !!!
582 SDLSetWindowIcon(program.icon_filename);
584 // set window and icon title
588 static void SDLInitVideoBuffer_DrawBuffer(void)
590 /* SDL cannot directly draw to the visible video framebuffer like X11,
591 but always uses a backbuffer, which is then blitted to the visible
592 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
593 visible video framebuffer with 'SDL_Flip', if the hardware supports
594 this). Therefore do not use an additional backbuffer for drawing, but
595 use a symbolic buffer (distinguishable from the SDL backbuffer) called
596 'window', which indicates that the SDL backbuffer should be updated to
597 the visible video framebuffer when attempting to blit to it.
599 For convenience, it seems to be a good idea to create this symbolic
600 buffer 'window' at the same size as the SDL backbuffer. Although it
601 should never be drawn to directly, it would do no harm nevertheless. */
603 // create additional (symbolic) buffer for double-buffering
604 ReCreateBitmap(&window, video.width, video.height);
606 // create dummy drawing buffer for headless mode, if needed
607 if (program.headless)
608 ReCreateBitmap(&backbuffer, video.width, video.height);
611 void SDLInitVideoBuffer(boolean fullscreen)
613 SDLInitVideoBuffer_VideoBuffer(fullscreen);
614 SDLInitVideoBuffer_DrawBuffer();
617 static boolean SDLCreateScreen(boolean fullscreen)
619 SDL_Surface *new_surface = NULL;
621 int surface_flags_window = SURFACE_FLAGS;
622 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
625 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
627 video.vsync_mode = VSYNC_MODE_OFF;
629 if (!strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF))
631 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
632 video.vsync_mode = VSYNC_MODE_NORMAL;
635 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
636 _without_ enabling 2D/3D acceleration and/or guest additions installed,
637 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
638 it will try to use accelerated graphics and apparently fails miserably) */
639 int renderer_flags = SDL_RENDERER_SOFTWARE;
642 int width = video.width;
643 int height = video.height;
644 int screen_width = video.screen_width;
645 int screen_height = video.screen_height;
646 int surface_flags = (fullscreen ? surface_flags_fullscreen :
647 surface_flags_window);
648 int display_nr = options.display_nr;
650 // default window size is unscaled
651 video.window_width = screen_width;
652 video.window_height = screen_height;
654 // store if initial screen mode is fullscreen mode when changing screen size
655 video.fullscreen_initial = fullscreen;
657 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
659 video.window_width = window_scaling_factor * screen_width;
660 video.window_height = window_scaling_factor * screen_height;
662 if (sdl_texture_stream)
664 SDL_DestroyTexture(sdl_texture_stream);
665 sdl_texture_stream = NULL;
668 if (sdl_texture_target)
670 SDL_DestroyTexture(sdl_texture_target);
671 sdl_texture_target = NULL;
674 if (!(fullscreen && fullscreen_enabled))
678 SDL_DestroyRenderer(sdl_renderer);
684 SDL_SetWindowSize(sdl_window, video.window_width, video.window_height);
688 if (sdl_window == NULL)
689 sdl_window = SDL_CreateWindow(program.window_title,
690 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
691 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
696 if (sdl_window != NULL)
698 if (sdl_renderer == NULL)
699 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
701 if (sdl_renderer != NULL)
703 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
704 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
706 // required for setting adaptive vsync when using OpenGL renderer
707 SDLSetScreenVsyncMode(setup.vsync_mode);
709 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
710 SDL_PIXELFORMAT_ARGB8888,
711 SDL_TEXTUREACCESS_STREAMING,
714 if (SDL_RenderTargetSupported(sdl_renderer))
715 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
716 SDL_PIXELFORMAT_ARGB8888,
717 SDL_TEXTUREACCESS_TARGET,
720 if (sdl_texture_stream != NULL)
722 // use SDL default values for RGB masks and no alpha channel
723 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
725 if (new_surface == NULL)
726 Warn("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
730 Warn("SDL_CreateTexture() failed: %s", SDL_GetError());
735 Warn("SDL_CreateRenderer() failed: %s", SDL_GetError());
740 Warn("SDL_CreateWindow() failed: %s", SDL_GetError());
743 SDLSetScreenProperties();
745 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
746 if (new_surface != NULL)
747 fullscreen_enabled = fullscreen;
749 if (backbuffer == NULL)
750 backbuffer = CreateBitmapStruct();
752 backbuffer->width = video.width;
753 backbuffer->height = video.height;
755 if (backbuffer->surface)
756 SDL_FreeSurface(backbuffer->surface);
758 backbuffer->surface = new_surface;
760 return (new_surface != NULL);
763 boolean SDLSetVideoMode(boolean fullscreen)
765 boolean success = FALSE;
769 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
771 // switch display to fullscreen mode, if available
772 success = SDLCreateScreen(TRUE);
776 // switching display to fullscreen mode failed -- do not try it again
777 video.fullscreen_available = FALSE;
781 video.fullscreen_enabled = TRUE;
785 if ((!fullscreen && video.fullscreen_enabled) || !success)
787 // switch display to window mode
788 success = SDLCreateScreen(FALSE);
792 // switching display to window mode failed -- should not happen
796 video.fullscreen_enabled = FALSE;
797 video.window_scaling_percent = setup.window_scaling_percent;
798 video.window_scaling_quality = setup.window_scaling_quality;
800 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
804 SDLRedrawWindow(); // map window
809 void SDLSetWindowTitle(void)
811 if (sdl_window == NULL)
814 SDL_SetWindowTitle(sdl_window, program.window_title);
817 void SDLSetWindowScaling(int window_scaling_percent)
819 if (sdl_window == NULL)
822 float window_scaling_factor = (float)window_scaling_percent / 100;
823 int new_window_width = (int)(window_scaling_factor * video.screen_width);
824 int new_window_height = (int)(window_scaling_factor * video.screen_height);
826 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
828 video.window_scaling_percent = window_scaling_percent;
829 video.window_width = new_window_width;
830 video.window_height = new_window_height;
835 void SDLSetWindowScalingQuality(char *window_scaling_quality)
837 SDL_Texture *new_texture;
839 if (sdl_texture_stream == NULL)
842 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
844 new_texture = SDL_CreateTexture(sdl_renderer,
845 SDL_PIXELFORMAT_ARGB8888,
846 SDL_TEXTUREACCESS_STREAMING,
847 video.width, video.height);
849 if (new_texture != NULL)
851 SDL_DestroyTexture(sdl_texture_stream);
853 sdl_texture_stream = new_texture;
856 if (SDL_RenderTargetSupported(sdl_renderer))
857 new_texture = SDL_CreateTexture(sdl_renderer,
858 SDL_PIXELFORMAT_ARGB8888,
859 SDL_TEXTUREACCESS_TARGET,
860 video.width, video.height);
864 if (new_texture != NULL)
866 SDL_DestroyTexture(sdl_texture_target);
868 sdl_texture_target = new_texture;
873 video.window_scaling_quality = window_scaling_quality;
876 void SDLSetWindowFullscreen(boolean fullscreen)
878 if (sdl_window == NULL)
881 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
883 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
884 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
886 // if screen size was changed in fullscreen mode, correct desktop window size
887 if (!fullscreen && video.fullscreen_initial)
889 SDLSetWindowScaling(setup.window_scaling_percent);
890 SDL_SetWindowPosition(sdl_window,
891 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr),
892 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr));
894 video.fullscreen_initial = FALSE;
898 void SDLSetDisplaySize(void)
900 if (sdl_renderer != NULL)
904 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
906 video.display_width = w;
907 video.display_height = h;
910 Debug("video", "SDL renderer size: %d x %d",
911 video.display_width, video.display_height);
916 SDL_Rect display_bounds;
918 SDL_GetDisplayBounds(0, &display_bounds);
920 video.display_width = display_bounds.w;
921 video.display_height = display_bounds.h;
924 Debug("video", "SDL display size: %d x %d",
925 video.display_width, video.display_height);
930 void SDLSetScreenSizeAndOffsets(int width, int height)
932 // set default video screen size and offsets
933 video.screen_width = width;
934 video.screen_height = height;
935 video.screen_xoffset = 0;
936 video.screen_yoffset = 0;
938 #if defined(USE_COMPLETE_DISPLAY)
939 float ratio_video = (float) width / height;
940 float ratio_display = (float) video.display_width / video.display_height;
942 if (ratio_video != ratio_display)
944 // adjust drawable screen size to cover the whole device display
946 if (ratio_video < ratio_display)
947 video.screen_width *= ratio_display / ratio_video;
949 video.screen_height *= ratio_video / ratio_display;
951 video.screen_xoffset = (video.screen_width - width) / 2;
952 video.screen_yoffset = (video.screen_height - height) / 2;
955 Debug("video", "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
957 video.screen_width, video.screen_height,
958 ratio_video, ratio_display);
964 void SDLSetScreenSizeForRenderer(int width, int height)
966 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
969 void SDLSetScreenProperties(void)
972 SDLSetScreenSizeAndOffsets(video.width, video.height);
973 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
975 SetOverlayGridSizeAndButtons();
978 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
980 video.screen_rendering_mode =
981 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
982 SPECIAL_RENDERING_BITMAP :
983 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
984 SPECIAL_RENDERING_TARGET:
985 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
986 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
989 void SDLSetScreenVsyncMode(char *vsync_mode)
991 // changing vsync mode without re-creating renderer only supported by OpenGL
992 if (!strPrefixLower((char *)SDLGetRendererName(), "opengl"))
995 int interval = VSYNC_MODE_STR_TO_INT(vsync_mode);
996 int result = SDL_GL_SetSwapInterval(interval);
998 // if adaptive vsync requested, but not supported, retry with normal vsync
999 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
1001 interval = VSYNC_MODE_NORMAL;
1003 result = SDL_GL_SetSwapInterval(interval);
1007 interval = VSYNC_MODE_OFF;
1009 video.vsync_mode = interval;
1012 void SDLRedrawWindow(void)
1014 UpdateScreen_WithoutFrameDelay(NULL);
1017 void SDLFreeBitmapPointers(Bitmap *bitmap)
1019 if (bitmap->surface)
1020 SDL_FreeSurface(bitmap->surface);
1021 if (bitmap->surface_masked)
1022 SDL_FreeSurface(bitmap->surface_masked);
1024 bitmap->surface = NULL;
1025 bitmap->surface_masked = NULL;
1027 if (bitmap->texture)
1028 SDL_DestroyTexture(bitmap->texture);
1029 if (bitmap->texture_masked)
1030 SDL_DestroyTexture(bitmap->texture_masked);
1032 bitmap->texture = NULL;
1033 bitmap->texture_masked = NULL;
1036 void SDLBlitSurface(SDL_Surface *src_surface, SDL_Surface *dst_surface,
1037 int src_x, int src_y, int width, int height,
1038 int dst_x, int dst_y)
1040 SDL_Rect src_rect, dst_rect;
1045 src_rect.h = height;
1050 dst_rect.h = height;
1052 SDL_BlitSurface(src_surface, &src_rect, dst_surface, &dst_rect);
1055 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1056 int src_x, int src_y, int width, int height,
1057 int dst_x, int dst_y, int mask_mode)
1059 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1060 SDL_Rect src_rect, dst_rect;
1065 src_rect.h = height;
1070 dst_rect.h = height;
1072 SDLSetBitmapAlpha(src_bitmap, FALSE, mask_mode == BLIT_MASKED);
1074 // if (src_bitmap != backbuffer || dst_bitmap != window)
1075 if (!(src_bitmap == backbuffer && dst_bitmap == window))
1076 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
1077 src_bitmap->surface_masked : src_bitmap->surface),
1078 &src_rect, real_dst_bitmap->surface, &dst_rect);
1080 if (dst_bitmap == window)
1081 UpdateScreen_WithFrameDelay(&dst_rect);
1084 void SDLBlitTexture(Bitmap *bitmap,
1085 int src_x, int src_y, int width, int height,
1086 int dst_x, int dst_y, int mask_mode)
1088 SDL_Texture *texture;
1093 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1095 if (texture == NULL)
1101 src_rect.h = height;
1106 dst_rect.h = height;
1108 SDLSetBitmapAlpha(bitmap, TRUE, mask_mode == BLIT_MASKED);
1110 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1113 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1116 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1124 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1126 if (dst_bitmap == window)
1127 UpdateScreen_WithFrameDelay(&rect);
1130 void PrepareFadeBitmap(int draw_target)
1132 Bitmap *fade_bitmap =
1133 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1134 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1136 if (fade_bitmap == NULL)
1139 // copy backbuffer to fading buffer
1140 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1142 // add border and animations to fading buffer
1143 FinalizeScreen(draw_target);
1146 void SDLFadeRectangle(int x, int y, int width, int height,
1147 int fade_mode, int fade_delay, int post_delay,
1148 void (*draw_border_function)(void))
1150 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1151 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1152 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1153 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1154 SDL_Surface *surface_screen = backbuffer->surface;
1155 SDL_Rect src_rect, dst_rect;
1157 int src_x = x, src_y = y;
1158 int dst_x = x, dst_y = y;
1159 unsigned int time_last, time_current;
1161 // store function for drawing global masked border
1162 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1164 // deactivate drawing of global border while fading, if needed
1165 if (draw_border_function == NULL)
1166 gfx.draw_global_border_function = NULL;
1171 src_rect.h = height;
1175 dst_rect.w = width; // (ignored)
1176 dst_rect.h = height; // (ignored)
1178 dst_rect2 = dst_rect;
1180 // before fading in, store backbuffer (without animation graphics)
1181 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1182 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1184 // copy source and target surfaces to temporary surfaces for fading
1185 if (fade_mode & FADE_TYPE_TRANSFORM)
1187 // (source and target fading buffer already prepared)
1189 else if (fade_mode & FADE_TYPE_FADE_IN)
1191 // (target fading buffer already prepared)
1192 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1194 else // FADE_TYPE_FADE_OUT
1196 // (source fading buffer already prepared)
1197 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1200 time_current = SDL_GetTicks();
1202 if (fade_delay <= 0)
1204 // immediately draw final target frame without delay
1205 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1209 // when fading without delay, also skip post delay
1213 if (fade_mode == FADE_MODE_MELT)
1215 boolean done = FALSE;
1216 int melt_pixels = 2;
1217 int melt_columns = width / melt_pixels;
1218 int ypos[melt_columns];
1219 int max_steps = height / 8 + 32;
1224 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1226 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1228 ypos[0] = -GetSimpleRandom(16);
1230 for (i = 1 ; i < melt_columns; i++)
1232 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1234 ypos[i] = ypos[i - 1] + r;
1247 time_last = time_current;
1248 time_current = SDL_GetTicks();
1249 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1250 steps_final = MIN(MAX(0, steps), max_steps);
1254 done = (steps_done >= steps_final);
1256 for (i = 0 ; i < melt_columns; i++)
1264 else if (ypos[i] < height)
1269 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1271 if (ypos[i] + dy >= height)
1272 dy = height - ypos[i];
1274 // copy part of (appearing) target surface to upper area
1275 src_rect.x = src_x + i * melt_pixels;
1276 // src_rect.y = src_y + ypos[i];
1278 src_rect.w = melt_pixels;
1280 src_rect.h = ypos[i] + dy;
1282 dst_rect.x = dst_x + i * melt_pixels;
1283 // dst_rect.y = dst_y + ypos[i];
1286 if (steps_done >= steps_final)
1287 SDL_BlitSurface(surface_target, &src_rect,
1288 surface_screen, &dst_rect);
1292 // copy part of (disappearing) source surface to lower area
1293 src_rect.x = src_x + i * melt_pixels;
1295 src_rect.w = melt_pixels;
1296 src_rect.h = height - ypos[i];
1298 dst_rect.x = dst_x + i * melt_pixels;
1299 dst_rect.y = dst_y + ypos[i];
1301 if (steps_done >= steps_final)
1302 SDL_BlitSurface(surface_source, &src_rect,
1303 surface_screen, &dst_rect);
1309 src_rect.x = src_x + i * melt_pixels;
1311 src_rect.w = melt_pixels;
1312 src_rect.h = height;
1314 dst_rect.x = dst_x + i * melt_pixels;
1317 if (steps_done >= steps_final)
1318 SDL_BlitSurface(surface_target, &src_rect,
1319 surface_screen, &dst_rect);
1323 if (steps_done >= steps_final)
1325 if (draw_border_function != NULL)
1326 draw_border_function();
1328 UpdateScreen_WithFrameDelay(&dst_rect2);
1330 if (PendingEscapeKeyEvent())
1335 else if (fade_mode == FADE_MODE_CURTAIN)
1339 int xx_size = width / 2;
1341 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1343 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1345 for (xx = 0; xx < xx_size;)
1347 time_last = time_current;
1348 time_current = SDL_GetTicks();
1349 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1350 xx_final = MIN(MAX(0, xx), xx_size);
1355 src_rect.h = height;
1360 // draw new (target) image to screen buffer
1361 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1363 if (xx_final < xx_size)
1365 src_rect.w = xx_size - xx_final;
1366 src_rect.h = height;
1368 // draw old (source) image to screen buffer (left side)
1370 src_rect.x = src_x + xx_final;
1373 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1375 // draw old (source) image to screen buffer (right side)
1377 src_rect.x = src_x + xx_size;
1378 dst_rect.x = dst_x + xx_size + xx_final;
1380 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1383 if (draw_border_function != NULL)
1384 draw_border_function();
1386 // only update the region of the screen that is affected from fading
1387 UpdateScreen_WithFrameDelay(&dst_rect2);
1389 if (PendingEscapeKeyEvent())
1393 else // fading in, fading out or cross-fading
1398 for (alpha = 0.0; alpha < 255.0;)
1400 time_last = time_current;
1401 time_current = SDL_GetTicks();
1402 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1403 alpha_final = MIN(MAX(0, alpha), 255);
1405 // draw existing (source) image to screen buffer
1406 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1408 // draw new (target) image to screen buffer using alpha blending
1409 SDLSetAlpha(surface_target, TRUE, alpha_final);
1410 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1412 if (draw_border_function != NULL)
1413 draw_border_function();
1415 // only update the region of the screen that is affected from fading
1416 UpdateScreen_WithFrameDelay(&dst_rect);
1418 if (PendingEscapeKeyEvent())
1424 Delay_WithScreenUpdates(post_delay);
1426 // restore function for drawing global masked border
1427 gfx.draw_global_border_function = draw_global_border_function;
1429 // after fading in, restore backbuffer (without animation graphics)
1430 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1431 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1434 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1435 int to_x, int to_y, Uint32 color)
1437 SDL_Surface *surface = dst_bitmap->surface;
1441 swap_numbers(&from_x, &to_x);
1444 swap_numbers(&from_y, &to_y);
1448 rect.w = (to_x - from_x + 1);
1449 rect.h = (to_y - from_y + 1);
1451 SDL_FillRect(surface, &rect, color);
1454 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1455 int to_x, int to_y, Uint32 color)
1457 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1460 #if ENABLE_UNUSED_CODE
1461 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1462 int num_points, Uint32 color)
1467 for (i = 0; i < num_points - 1; i++)
1469 for (x = 0; x < line_width; x++)
1471 for (y = 0; y < line_width; y++)
1473 int dx = x - line_width / 2;
1474 int dy = y - line_width / 2;
1476 if ((x == 0 && y == 0) ||
1477 (x == 0 && y == line_width - 1) ||
1478 (x == line_width - 1 && y == 0) ||
1479 (x == line_width - 1 && y == line_width - 1))
1482 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1483 points[i + 1].x + dx, points[i + 1].y + dy, color);
1490 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1492 SDL_Surface *surface = src_bitmap->surface;
1494 switch (surface->format->BytesPerPixel)
1496 case 1: // assuming 8-bpp
1498 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1502 case 2: // probably 15-bpp or 16-bpp
1504 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1508 case 3: // slow 24-bpp mode; usually not used
1511 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1515 shift = surface->format->Rshift;
1516 color |= *(pix + shift / 8) >> shift;
1517 shift = surface->format->Gshift;
1518 color |= *(pix + shift / 8) >> shift;
1519 shift = surface->format->Bshift;
1520 color |= *(pix + shift / 8) >> shift;
1526 case 4: // probably 32-bpp
1528 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1537 // ============================================================================
1538 // The following functions were taken from the SGE library
1539 // (SDL Graphics Extension Library) by Anders Lindström
1540 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1541 // ============================================================================
1543 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1545 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1547 switch (surface->format->BytesPerPixel)
1552 *((Uint8 *)surface->pixels + y * surface->pitch + x) = color;
1558 // Probably 15-bpp or 16-bpp
1559 *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x) = color;
1565 // Slow 24-bpp mode, usually not used
1569 // Gack - slow, but endian correct
1570 pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1571 shift = surface->format->Rshift;
1572 *(pix + shift / 8) = color>>shift;
1573 shift = surface->format->Gshift;
1574 *(pix + shift / 8) = color>>shift;
1575 shift = surface->format->Bshift;
1576 *(pix + shift / 8) = color>>shift;
1583 *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x) = color;
1591 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1592 Uint8 R, Uint8 G, Uint8 B)
1594 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1597 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1599 *((Uint8 *)surface->pixels + y * surface->pitch + x) = color;
1602 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1604 *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x) = color;
1607 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1612 // Gack - slow, but endian correct
1613 pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1614 shift = surface->format->Rshift;
1615 *(pix + shift / 8) = color>>shift;
1616 shift = surface->format->Gshift;
1617 *(pix + shift / 8) = color>>shift;
1618 shift = surface->format->Bshift;
1619 *(pix + shift / 8) = color>>shift;
1622 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1624 *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x) = color;
1627 static void _PutPixelX(SDL_Surface *dest, Sint16 x, Sint16 y, Uint32 color)
1629 switch (dest->format->BytesPerPixel)
1632 *((Uint8 *)dest->pixels + y * dest->pitch + x) = color;
1636 *((Uint16 *)dest->pixels + y * dest->pitch / 2 + x) = color;
1640 _PutPixel24(dest, x, y, color);
1644 *((Uint32 *)dest->pixels + y * dest->pitch / 4 + x) = color;
1650 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1652 if (SDL_MUSTLOCK(surface))
1654 if (SDL_LockSurface(surface) < 0)
1660 _PutPixel(surface, x, y, color);
1662 if (SDL_MUSTLOCK(surface))
1664 SDL_UnlockSurface(surface);
1669 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1670 Uint8 r, Uint8 g, Uint8 b)
1672 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1675 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1677 if (y >= 0 && y <= dest->h - 1)
1679 switch (dest->format->BytesPerPixel)
1682 return y * dest->pitch;
1686 return y * dest->pitch / 2;
1690 return y * dest->pitch;
1694 return y * dest->pitch / 4;
1702 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1705 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1707 switch (surface->format->BytesPerPixel)
1712 *((Uint8 *)surface->pixels + ypitch + x) = color;
1718 // Probably 15-bpp or 16-bpp
1719 *((Uint16 *)surface->pixels + ypitch + x) = color;
1725 // Slow 24-bpp mode, usually not used
1729 // Gack - slow, but endian correct
1730 pix = (Uint8 *)surface->pixels + ypitch + x * 3;
1731 shift = surface->format->Rshift;
1732 *(pix + shift / 8) = color>>shift;
1733 shift = surface->format->Gshift;
1734 *(pix + shift / 8) = color>>shift;
1735 shift = surface->format->Bshift;
1736 *(pix + shift / 8) = color>>shift;
1743 *((Uint32 *)surface->pixels + ypitch + x) = color;
1750 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1755 if (SDL_MUSTLOCK(Surface))
1757 if (SDL_LockSurface(Surface) < 0)
1771 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1775 if (x2 > Surface->w - 1)
1776 x2 = Surface->w - 1;
1783 SDL_FillRect(Surface, &l, Color);
1785 if (SDL_MUSTLOCK(Surface))
1787 SDL_UnlockSurface(Surface);
1791 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1792 Uint8 R, Uint8 G, Uint8 B)
1794 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1797 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1810 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1814 if (x2 > Surface->w - 1)
1815 x2 = Surface->w - 1;
1822 SDL_FillRect(Surface, &l, Color);
1825 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1830 if (SDL_MUSTLOCK(Surface))
1832 if (SDL_LockSurface(Surface) < 0)
1846 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1850 if (y2 > Surface->h - 1)
1851 y2 = Surface->h - 1;
1858 SDL_FillRect(Surface, &l, Color);
1860 if (SDL_MUSTLOCK(Surface))
1862 SDL_UnlockSurface(Surface);
1866 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1867 Uint8 R, Uint8 G, Uint8 B)
1869 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1872 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1885 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1889 if (y2 > Surface->h - 1)
1890 y2 = Surface->h - 1;
1897 SDL_FillRect(Surface, &l, Color);
1901 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1902 Sint16 x2, Sint16 y2, Uint32 Color,
1903 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1906 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1911 sdx = (dx < 0) ? -1 : 1;
1912 sdy = (dy < 0) ? -1 : 1;
1924 for (x = 0; x < dx; x++)
1926 Callback(Surface, px, py, Color);
1940 for (y = 0; y < dy; y++)
1942 Callback(Surface, px, py, Color);
1957 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1958 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1959 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1962 sge_DoLine(Surface, X1, Y1, X2, Y2,
1963 SDL_MapRGB(Surface->format, R, G, B), Callback);
1967 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1970 if (SDL_MUSTLOCK(Surface))
1972 if (SDL_LockSurface(Surface) < 0)
1977 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1979 // unlock the display
1980 if (SDL_MUSTLOCK(Surface))
1982 SDL_UnlockSurface(Surface);
1987 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1988 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1990 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1994 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1996 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
2000 // ----------------------------------------------------------------------------
2001 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
2002 // ----------------------------------------------------------------------------
2004 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
2005 int src_x, int src_y, int width, int height,
2006 int dst_x, int dst_y)
2010 for (y = 0; y < height; y++)
2012 for (x = 0; x < width; x++)
2014 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
2016 if (pixel != BLACK_PIXEL)
2017 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
2023 // ============================================================================
2024 // The following functions were taken from the SDL_gfx library version 2.0.3
2025 // (Rotozoomer) by Andreas Schiffler
2026 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
2027 // ============================================================================
2029 // ----------------------------------------------------------------------------
2032 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
2033 // ----------------------------------------------------------------------------
2043 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
2046 tColorRGBA *sp, *csp, *dp;
2050 sp = csp = (tColorRGBA *) src->pixels;
2051 dp = (tColorRGBA *) dst->pixels;
2052 dgap = dst->pitch - dst->w * 4;
2054 for (y = 0; y < dst->h; y++)
2058 for (x = 0; x < dst->w; x++)
2060 tColorRGBA *sp0 = sp;
2061 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
2062 tColorRGBA *sp00 = &sp0[0];
2063 tColorRGBA *sp01 = &sp0[1];
2064 tColorRGBA *sp10 = &sp1[0];
2065 tColorRGBA *sp11 = &sp1[1];
2068 // create new color pixel from all four source color pixels
2069 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
2070 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
2071 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
2072 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
2077 // advance source pointers
2080 // advance destination pointer
2084 // advance source pointer
2085 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2087 // advance destination pointers
2088 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2094 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2096 int x, y, *sax, *say, *csax, *csay;
2098 tColorRGBA *sp, *csp, *csp0, *dp;
2101 // use specialized zoom function when scaling down to exactly half size
2102 if (src->w == 2 * dst->w &&
2103 src->h == 2 * dst->h)
2104 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2107 sx = (float) src->w / (float) dst->w;
2108 sy = (float) src->h / (float) dst->h;
2110 // allocate memory for row increments
2111 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2112 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2114 // precalculate row increments
2115 for (x = 0; x <= dst->w; x++)
2116 *csax++ = (int)(sx * x);
2118 for (y = 0; y <= dst->h; y++)
2119 *csay++ = (int)(sy * y);
2122 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2123 dp = (tColorRGBA *) dst->pixels;
2124 dgap = dst->pitch - dst->w * 4;
2127 for (y = 0; y < dst->h; y++)
2132 for (x = 0; x < dst->w; x++)
2137 // advance source pointers
2141 // advance destination pointer
2145 // advance source pointer
2147 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2149 // advance destination pointers
2150 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2159 // ----------------------------------------------------------------------------
2162 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2163 // ----------------------------------------------------------------------------
2165 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2167 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2168 Uint8 *sp, *dp, *csp;
2172 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2173 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2175 // allocate memory for row increments
2176 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2177 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2179 // precalculate row increments
2182 for (x = 0; x < dst->w; x++)
2185 *csax = (csx >> 16);
2192 for (y = 0; y < dst->h; y++)
2195 *csay = (csy >> 16);
2202 for (x = 0; x < dst->w; x++)
2210 for (y = 0; y < dst->h; y++)
2217 sp = csp = (Uint8 *) src->pixels;
2218 dp = (Uint8 *) dst->pixels;
2219 dgap = dst->pitch - dst->w;
2223 for (y = 0; y < dst->h; y++)
2227 for (x = 0; x < dst->w; x++)
2232 // advance source pointers
2236 // advance destination pointer
2240 // advance source pointer (for row)
2241 csp += ((*csay) * src->pitch);
2244 // advance destination pointers
2254 // ----------------------------------------------------------------------------
2257 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2258 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2259 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2260 // into a 32bit RGBA format on the fly.
2261 // ----------------------------------------------------------------------------
2263 SDL_Surface *SDLZoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2265 SDL_Surface *zoom_src = NULL;
2266 SDL_Surface *zoom_dst = NULL;
2267 boolean is_converted = FALSE;
2274 // determine if source surface is 32 bit or 8 bit
2275 is_32bit = (src->format->BitsPerPixel == 32);
2277 if (is_32bit || src->format->BitsPerPixel == 8)
2279 // use source surface 'as is'
2284 // new source surface is 32 bit with a defined RGB ordering
2285 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2286 0x000000ff, 0x0000ff00, 0x00ff0000,
2287 (src->format->Amask ? 0xff000000 : 0));
2288 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2290 is_converted = TRUE;
2293 // allocate surface to completely contain the zoomed surface
2296 // target surface is 32 bit with source RGBA/ABGR ordering
2297 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2298 zoom_src->format->Rmask,
2299 zoom_src->format->Gmask,
2300 zoom_src->format->Bmask,
2301 zoom_src->format->Amask);
2305 // target surface is 8 bit
2306 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2310 // lock source surface
2311 SDL_LockSurface(zoom_src);
2313 // check which kind of surface we have
2316 // call the 32 bit transformation routine to do the zooming
2317 zoomSurfaceRGBA(zoom_src, zoom_dst);
2322 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2323 zoom_dst->format->palette->colors[i] =
2324 zoom_src->format->palette->colors[i];
2325 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2327 // call the 8 bit transformation routine to do the zooming
2328 zoomSurfaceY(zoom_src, zoom_dst);
2331 // unlock source surface
2332 SDL_UnlockSurface(zoom_src);
2334 // free temporary surface
2336 SDL_FreeSurface(zoom_src);
2338 // return destination surface
2342 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2344 Bitmap *dst_bitmap = CreateBitmapStruct();
2345 SDL_Surface *src_surface = src_bitmap->surface_masked;
2346 SDL_Surface *dst_surface;
2348 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2349 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2351 dst_bitmap->width = dst_width;
2352 dst_bitmap->height = dst_height;
2354 // create zoomed temporary surface from source surface
2355 dst_surface = SDLZoomSurface(src_surface, dst_width, dst_height);
2357 // create native format destination surface from zoomed temporary surface
2358 SDLSetNativeSurface(&dst_surface);
2360 // set color key for zoomed surface from source surface, if defined
2361 if (SDLHasColorKey(src_surface))
2362 SDLCopyColorKey(src_surface, dst_surface);
2364 // create native non-transparent surface for opaque blitting
2365 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2367 // set native transparent surface for masked blitting
2368 dst_bitmap->surface_masked = dst_surface;
2374 // ============================================================================
2375 // load image to bitmap
2376 // ============================================================================
2378 Bitmap *SDLLoadImage(char *filename)
2380 Bitmap *new_bitmap = CreateBitmapStruct();
2381 SDL_Surface *sdl_image_tmp;
2383 if (program.headless)
2385 // prevent sanity check warnings at later stage
2386 new_bitmap->width = new_bitmap->height = 1;
2391 print_timestamp_init("SDLLoadImage");
2393 print_timestamp_time(getBaseNamePtr(filename));
2395 // load image to temporary surface
2396 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2397 Fail("IMG_Load('%s') failed: %s", getBaseNamePtr(filename), SDL_GetError());
2399 print_timestamp_time("IMG_Load");
2401 UPDATE_BUSY_STATE();
2403 // create native non-transparent surface for current image
2404 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2405 Fail("SDLGetOpaqueSurface() failed");
2407 print_timestamp_time("SDLGetNativeSurface (opaque)");
2409 UPDATE_BUSY_STATE();
2411 // set black pixel to transparent if no alpha channel / transparent color
2412 if (!SDLHasAlpha(sdl_image_tmp) &&
2413 !SDLHasColorKey(sdl_image_tmp))
2414 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2415 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2417 // create native transparent surface for current image
2418 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2419 Fail("SDLGetNativeSurface() failed");
2421 print_timestamp_time("SDLGetNativeSurface (masked)");
2423 UPDATE_BUSY_STATE();
2425 // free temporary surface
2426 SDL_FreeSurface(sdl_image_tmp);
2428 new_bitmap->width = new_bitmap->surface->w;
2429 new_bitmap->height = new_bitmap->surface->h;
2431 print_timestamp_done("SDLLoadImage");
2437 // ----------------------------------------------------------------------------
2438 // custom cursor fuctions
2439 // ----------------------------------------------------------------------------
2441 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2443 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2444 cursor_info->width, cursor_info->height,
2445 cursor_info->hot_x, cursor_info->hot_y);
2448 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2450 static struct MouseCursorInfo *last_cursor_info = NULL;
2451 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2452 static SDL_Cursor *cursor_default = NULL;
2453 static SDL_Cursor *cursor_current = NULL;
2455 // if invoked for the first time, store the SDL default cursor
2456 if (cursor_default == NULL)
2457 cursor_default = SDL_GetCursor();
2459 // only create new cursor if cursor info (custom only) has changed
2460 if (cursor_info != NULL && cursor_info != last_cursor_info)
2462 cursor_current = create_cursor(cursor_info);
2463 last_cursor_info = cursor_info;
2466 // only set new cursor if cursor info (custom or NULL) has changed
2467 if (cursor_info != last_cursor_info2)
2468 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2470 last_cursor_info2 = cursor_info;
2474 // ============================================================================
2476 // ============================================================================
2478 void SDLOpenAudio(void)
2480 if (program.headless)
2483 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2485 Warn("SDL_InitSubSystem() failed: %s", SDL_GetError());
2490 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2491 AUDIO_NUM_CHANNELS_STEREO,
2492 setup.system.audio_fragment_size) < 0)
2494 Warn("Mix_OpenAudio() failed: %s", SDL_GetError());
2499 audio.sound_available = TRUE;
2500 audio.music_available = TRUE;
2501 audio.loops_available = TRUE;
2502 audio.sound_enabled = TRUE;
2504 // set number of available mixer channels
2505 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2506 audio.music_channel = MUSIC_CHANNEL;
2507 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2509 Mixer_InitChannels();
2512 void SDLCloseAudio(void)
2515 Mix_HaltChannel(-1);
2518 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2522 // ============================================================================
2524 // ============================================================================
2526 void SDLWaitEvent(Event *event)
2528 SDL_WaitEvent(event);
2531 void SDLCorrectRawMousePosition(int *x, int *y)
2533 if (sdl_renderer == NULL)
2536 // this corrects the raw mouse position for logical screen size within event
2537 // filters (correction done later by SDL library when handling mouse events)
2540 float scale_x, scale_y;
2542 SDL_RenderGetViewport(sdl_renderer, &viewport);
2543 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2545 *x = (int)(*x / scale_x);
2546 *y = (int)(*y / scale_y);
2553 // ============================================================================
2554 // joystick functions
2555 // ============================================================================
2557 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2558 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2559 static int sdl_js_axis[MAX_PLAYERS][2];
2560 static int sdl_js_button[MAX_PLAYERS][2];
2561 static boolean sdl_is_controller[MAX_PLAYERS];
2563 void SDLClearJoystickState(void)
2567 for (i = 0; i < MAX_PLAYERS; i++)
2569 for (j = 0; j < 2; j++)
2571 sdl_js_axis_raw[i][j] = -1;
2572 sdl_js_axis[i][j] = 0;
2573 sdl_js_button[i][j] = 0;
2578 boolean SDLOpenJoystick(int nr)
2580 if (nr < 0 || nr >= MAX_PLAYERS)
2583 sdl_is_controller[nr] = SDL_IsGameController(nr);
2586 Debug("joystick", "opening joystick %d (%s)",
2587 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2590 if (sdl_is_controller[nr])
2591 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2593 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2595 return (sdl_joystick[nr] != NULL);
2598 void SDLCloseJoystick(int nr)
2600 if (nr < 0 || nr >= MAX_PLAYERS)
2604 Debug("joystick", "closing joystick %d", nr);
2607 if (sdl_is_controller[nr])
2608 SDL_GameControllerClose(sdl_joystick[nr]);
2610 SDL_JoystickClose(sdl_joystick[nr]);
2612 sdl_joystick[nr] = NULL;
2615 boolean SDLCheckJoystickOpened(int nr)
2617 if (nr < 0 || nr >= MAX_PLAYERS)
2620 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2623 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2625 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2626 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2627 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2628 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2630 if (nr < 0 || nr >= MAX_PLAYERS)
2636 // prevent (slightly jittering, but centered) axis A from resetting axis B
2637 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2638 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2641 sdl_js_axis[nr][axis_id] = axis_value;
2642 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2645 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2647 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2648 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2649 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2650 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2651 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2652 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2653 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2654 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2657 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2658 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2659 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2660 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2661 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2662 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2663 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2664 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2666 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2667 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2668 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2669 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2670 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2672 if (nr < 0 || nr >= MAX_PLAYERS)
2675 if (button_id == -1)
2678 sdl_js_button[nr][button_id] = button_state;
2681 void HandleJoystickEvent(Event *event)
2683 // when using joystick, disable overlay touch buttons
2684 runtime.uses_touch_device = FALSE;
2686 switch (event->type)
2688 case SDL_CONTROLLERDEVICEADDED:
2690 Debug("joystick", "SDL_CONTROLLERDEVICEADDED: device %d added",
2691 event->cdevice.which);
2696 case SDL_CONTROLLERDEVICEREMOVED:
2698 Debug("joystick", "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2699 event->cdevice.which);
2704 case SDL_CONTROLLERAXISMOTION:
2706 Debug("joystick", "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2707 event->caxis.which, event->caxis.axis, event->caxis.value);
2709 setJoystickAxis(event->caxis.which,
2711 event->caxis.value);
2714 case SDL_CONTROLLERBUTTONDOWN:
2716 Debug("joystick", "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2717 event->cbutton.which, event->cbutton.button);
2719 setJoystickButton(event->cbutton.which,
2720 event->cbutton.button,
2724 case SDL_CONTROLLERBUTTONUP:
2726 Debug("joystick", "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2727 event->cbutton.which, event->cbutton.button);
2729 setJoystickButton(event->cbutton.which,
2730 event->cbutton.button,
2734 case SDL_JOYAXISMOTION:
2735 if (sdl_is_controller[event->jaxis.which])
2739 Debug("joystick", "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2740 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2742 if (event->jaxis.axis < 4)
2743 setJoystickAxis(event->jaxis.which,
2745 event->jaxis.value);
2748 case SDL_JOYBUTTONDOWN:
2749 if (sdl_is_controller[event->jaxis.which])
2753 Debug("joystick", "SDL_JOYBUTTONDOWN: device %d, button %d",
2754 event->jbutton.which, event->jbutton.button);
2756 if (event->jbutton.button < 4)
2757 setJoystickButton(event->jbutton.which,
2758 event->jbutton.button,
2762 case SDL_JOYBUTTONUP:
2763 if (sdl_is_controller[event->jaxis.which])
2767 Debug("joystick", "SDL_JOYBUTTONUP: device %d, button %d",
2768 event->jbutton.which, event->jbutton.button);
2770 if (event->jbutton.button < 4)
2771 setJoystickButton(event->jbutton.which,
2772 event->jbutton.button,
2781 void SDLInitJoysticks(void)
2783 static boolean sdl_joystick_subsystem_initialized = FALSE;
2784 boolean print_warning = !sdl_joystick_subsystem_initialized;
2785 char *mappings_file_base = getPath2(options.conf_directory,
2786 GAMECONTROLLER_BASENAME);
2787 char *mappings_file_user = getPath2(getMainUserGameDataDir(),
2788 GAMECONTROLLER_BASENAME);
2792 if (!sdl_joystick_subsystem_initialized)
2794 sdl_joystick_subsystem_initialized = TRUE;
2796 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2798 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2799 Fail("SDL_Init() failed: %s", SDL_GetError());
2801 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2803 // the included game controller base mappings should always be found
2804 if (num_mappings == -1)
2805 Warn("no game controller base mappings found");
2808 Debug("joystick", "%d game controller base mapping(s) added",
2812 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2815 // the personal game controller user mappings may or may not be found
2816 if (num_mappings == -1)
2817 Warn("no game controller user mappings found");
2819 Debug("joystick", , "%d game controller user mapping(s) added",
2822 Debug("joystick", "%d joystick(s) found:", SDL_NumJoysticks());
2825 checked_free(mappings_file_base);
2826 checked_free(mappings_file_user);
2829 for (i = 0; i < SDL_NumJoysticks(); i++)
2831 const char *name, *type;
2833 if (SDL_IsGameController(i))
2835 name = SDL_GameControllerNameForIndex(i);
2836 type = "game controller";
2840 name = SDL_JoystickNameForIndex(i);
2844 Debug("joystick", "- joystick %d (%s): '%s'",
2845 i, type, (name ? name : "(Unknown)"));
2850 // assign joysticks from configured to connected joystick for all players
2851 for (i = 0; i < MAX_PLAYERS; i++)
2853 // get configured joystick for this player
2854 char *device_name = setup.input[i].joy.device_name;
2855 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2857 if (joystick_nr >= SDL_NumJoysticks())
2859 if (setup.input[i].use_joystick && print_warning)
2860 Warn("cannot find joystick %d", joystick_nr);
2865 // store configured joystick number for each player
2866 joystick.nr[i] = joystick_nr;
2869 // now open all connected joysticks (regardless if configured or not)
2870 for (i = 0; i < SDL_NumJoysticks(); i++)
2872 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2873 if (SDLCheckJoystickOpened(i))
2874 SDLCloseJoystick(i);
2876 if (SDLOpenJoystick(i))
2877 joystick.status = JOYSTICK_ACTIVATED;
2878 else if (print_warning)
2879 Warn("cannot open joystick %d", i);
2882 SDLClearJoystickState();
2885 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2887 if (nr < 0 || nr >= MAX_PLAYERS)
2891 *x = sdl_js_axis[nr][0];
2893 *y = sdl_js_axis[nr][1];
2896 *b1 = sdl_js_button[nr][0];
2898 *b2 = sdl_js_button[nr][1];
2904 // ============================================================================
2905 // touch input overlay functions
2906 // ============================================================================
2908 #if defined(USE_TOUCH_INPUT_OVERLAY)
2909 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2912 int grid_xsize = overlay.grid_xsize;
2913 int grid_ysize = overlay.grid_ysize;
2916 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2917 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2919 for (x = 0; x < grid_xsize; x++)
2921 rect.x = (x + 0) * video.screen_width / grid_xsize;
2922 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2924 for (y = 0; y < grid_ysize; y++)
2926 rect.y = (y + 0) * video.screen_height / grid_ysize;
2927 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2929 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2930 SDL_RenderDrawRect(sdl_renderer, &rect);
2934 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2937 static void RenderFillRectangle(int x, int y, int width, int height)
2939 SDL_Rect rect = { x, y, width, height };
2941 SDL_RenderFillRect(sdl_renderer, &rect);
2944 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2946 static int alpha_direction = 0;
2947 static int alpha_highlight = 0;
2948 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2949 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2951 int grid_xsize = overlay.grid_xsize;
2952 int grid_ysize = overlay.grid_ysize;
2955 if (alpha == alpha_max)
2957 if (alpha_direction < 0)
2959 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2961 if (alpha_highlight == 0)
2962 alpha_direction = 1;
2966 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2968 if (alpha_highlight == alpha_max)
2969 alpha_direction = -1;
2974 alpha_direction = 1;
2975 alpha_highlight = alpha;
2978 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2980 for (x = 0; x < grid_xsize; x++)
2982 for (y = 0; y < grid_ysize; y++)
2984 int grid_button = overlay.grid_button[x][y];
2985 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2986 int alpha_draw = alpha;
2987 int outline_border = MV_NONE;
2988 int border_size = 2;
2989 boolean draw_outlined = setup.touch.draw_outlined;
2990 boolean draw_pressed = setup.touch.draw_pressed;
2992 if (grid_button == CHAR_GRID_BUTTON_NONE)
2995 if (grid_button == overlay.grid_button_highlight)
2997 draw_outlined = FALSE;
2998 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
3001 if (draw_pressed && overlay.grid_button_action & grid_button_action)
3004 draw_outlined = FALSE;
3006 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
3009 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
3011 rect.x = (x + 0) * video.screen_width / grid_xsize;
3012 rect.y = (y + 0) * video.screen_height / grid_ysize;
3013 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
3014 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
3016 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
3018 rect.x += border_size;
3019 rect.w -= border_size;
3021 outline_border |= MV_LEFT;
3024 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
3026 rect.w -= border_size;
3028 outline_border |= MV_RIGHT;
3031 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
3033 rect.y += border_size;
3034 rect.h -= border_size;
3036 outline_border |= MV_UP;
3039 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
3041 rect.h -= border_size;
3043 outline_border |= MV_DOWN;
3048 int rect_x = rect.x +
3049 (outline_border & MV_LEFT ? border_size : 0);
3050 int rect_w = rect.w -
3051 (outline_border & MV_LEFT ? border_size : 0) -
3052 (outline_border & MV_RIGHT ? border_size : 0);
3054 if (outline_border & MV_LEFT)
3055 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
3057 if (outline_border & MV_RIGHT)
3058 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
3059 border_size, rect.h);
3061 if (outline_border & MV_UP)
3062 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
3064 if (outline_border & MV_DOWN)
3065 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
3066 rect_w, border_size);
3070 SDL_RenderFillRect(sdl_renderer, &rect);
3075 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3078 static void DrawTouchInputOverlay(void)
3080 static boolean deactivated = TRUE;
3081 static boolean show_grid = FALSE;
3082 static int alpha = 0;
3083 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3084 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3085 boolean active = (overlay.enabled && overlay.active);
3087 if (!active && deactivated)
3092 if (alpha < alpha_max)
3093 alpha = MIN(alpha + alpha_step, alpha_max);
3095 deactivated = FALSE;
3099 alpha = MAX(0, alpha - alpha_step);
3105 if (overlay.show_grid)
3107 else if (deactivated)
3111 DrawTouchInputOverlay_ShowGrid(alpha);
3113 DrawTouchInputOverlay_ShowGridButtons(alpha);
3116 static void DrawTouchGadgetsOverlay(void)
3118 DrawGadgets_OverlayTouchButtons();