1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
19 #define ENABLE_UNUSED_CODE 0 // currently unused functions
21 #define DEBUG_JOYSTICKS 0
24 // ============================================================================
26 // ============================================================================
28 // SDL internal variables
29 static SDL_Window *sdl_window = NULL;
30 static SDL_Renderer *sdl_renderer = NULL;
31 static SDL_Texture *sdl_texture_stream = NULL;
32 static SDL_Texture *sdl_texture_target = NULL;
33 static boolean fullscreen_enabled = FALSE;
34 static boolean limit_screen_updates = FALSE;
37 // functions from SGE library
38 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
40 #if defined(USE_TOUCH_INPUT_OVERLAY)
41 // functions to draw overlay graphics for touch device input
42 static void DrawTouchInputOverlay(void);
43 static void DrawTouchGadgetsOverlay(void);
46 void SDLLimitScreenUpdates(boolean enable)
48 limit_screen_updates = enable;
51 static void FinalizeScreen(int draw_target)
53 // copy global animations to render target buffer, if defined (below border)
54 if (gfx.draw_global_anim_function != NULL)
55 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
57 // copy global masked border to render target buffer, if defined
58 if (gfx.draw_global_border_function != NULL)
59 gfx.draw_global_border_function(draw_target);
61 // copy global animations to render target buffer, if defined (above border)
62 if (gfx.draw_global_anim_function != NULL)
63 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
65 // copy tile selection cursor to render target buffer, if defined (above all)
66 if (gfx.draw_tile_cursor_function != NULL)
67 gfx.draw_tile_cursor_function(draw_target);
69 // copy envelope request to render target buffer, if needed (above all)
70 if (gfx.draw_envelope_request_function != NULL)
71 gfx.draw_envelope_request_function(draw_target);
73 // copy global animations to render target buffer, if defined (mouse pointer)
74 if (gfx.draw_global_anim_function != NULL)
75 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_3);
78 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
83 static DelayCounter update_screen_delay = { 50 }; // (milliseconds)
84 SDL_Surface *screen = backbuffer->surface;
86 if (limit_screen_updates &&
87 !DelayReached(&update_screen_delay))
90 LimitScreenUpdates(FALSE);
94 static int LastFrameCounter = 0;
95 boolean changed = (FrameCounter != LastFrameCounter);
97 Debug("internal:frame", "FrameCounter == %d [%s]", FrameCounter,
98 (changed ? "-" : "SAME FRAME UPDATED"));
100 LastFrameCounter = FrameCounter;
103 if (FrameCounter % 2)
109 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
110 gfx.final_screen_bitmap != NULL) // may not be initialized yet
112 // draw global animations using bitmaps instead of using textures
113 // to prevent texture scaling artefacts (this is potentially slower)
115 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
116 gfx.win_xsize, gfx.win_ysize, 0, 0);
118 FinalizeScreen(DRAW_TO_SCREEN);
120 screen = gfx.final_screen_bitmap->surface;
122 // force full window redraw
126 SDL_Texture *sdl_texture = sdl_texture_stream;
128 // deactivate use of target texture if render targets are not supported
129 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
130 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
131 sdl_texture_target == NULL)
132 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
134 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
135 sdl_texture = sdl_texture_target;
139 int bytes_x = screen->pitch / video.width;
140 int bytes_y = screen->pitch;
142 SDL_UpdateTexture(sdl_texture, rect,
143 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
148 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
151 int xoff = video.screen_xoffset;
152 int yoff = video.screen_yoffset;
153 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
154 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
155 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
157 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
158 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
159 dst_rect2 = &dst_rect_screen;
161 dst_rect1 = &dst_rect_screen;
163 #if defined(HAS_SCREEN_KEYBOARD)
164 SDL_Rect src_rect_up = { 0, 0, video.width, video.height };
165 SDL_Rect dst_rect_up = dst_rect_screen;
167 if (video.shifted_up || video.shifted_up_delay.count)
169 int time_current = SDL_GetTicks();
170 int pos = video.shifted_up_pos;
171 int pos_last = video.shifted_up_pos_last;
173 if (!DelayReachedExt(&video.shifted_up_delay, time_current))
175 int delay_count = time_current - video.shifted_up_delay.count;
176 int delay_value = video.shifted_up_delay.value;
178 pos = pos_last + (pos - pos_last) * delay_count / delay_value;
182 video.shifted_up_pos_last = pos;
183 video.shifted_up_delay.count = 0;
187 src_rect_up.h = video.height - pos;
188 dst_rect_up.h = video.height - pos;
190 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
191 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
193 src_rect2 = &src_rect_up;
194 dst_rect2 = &dst_rect_up;
198 src_rect1 = &src_rect_up;
199 dst_rect1 = &dst_rect_up;
204 // clear render target buffer
205 SDL_RenderClear(sdl_renderer);
207 // set renderer to use target texture for rendering
208 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
209 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
210 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
212 // copy backbuffer texture to render target buffer
213 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
214 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
216 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
217 FinalizeScreen(DRAW_TO_SCREEN);
219 // when using target texture, copy it to screen buffer
220 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
221 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
223 SDL_SetRenderTarget(sdl_renderer, NULL);
224 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
227 #if defined(USE_TOUCH_INPUT_OVERLAY)
228 // draw overlay graphics for touch device input, if needed
229 DrawTouchInputOverlay();
231 // draw overlay gadgets for touch device input, if needed
232 DrawTouchGadgetsOverlay();
235 // global synchronization point of the game to align video frame delay
236 if (with_frame_delay)
237 WaitUntilDelayReached(&video.frame_delay);
239 video.frame_counter++;
241 // show render target buffer on screen
242 SDL_RenderPresent(sdl_renderer);
245 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
247 PumpEvents(); // execute event filter actions while waiting
249 UpdateScreenExt(rect, TRUE);
252 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
254 UpdateScreenExt(rect, FALSE);
257 void Delay_WithScreenUpdates(unsigned int delay)
259 unsigned int time_current = SDL_GetTicks();
260 unsigned int time_delayed = time_current + delay;
262 while (time_current < time_delayed)
264 // updating the screen contains waiting for frame delay (non-busy)
265 UpdateScreen_WithFrameDelay(NULL);
267 time_current = SDL_GetTicks();
271 static void SDLSetWindowIcon(char *basename)
273 // (setting the window icon on Mac OS X would replace the high-quality
274 // dock icon with the currently smaller (and uglier) icon from file)
276 #if !defined(PLATFORM_MAC)
277 char *filename = getCustomImageFilename(basename);
278 SDL_Surface *surface;
280 if (filename == NULL)
282 Warn("SDLSetWindowIcon(): cannot find file '%s'", basename);
287 if ((surface = IMG_Load(filename)) == NULL)
289 Warn("IMG_Load('%s') failed: %s", basename, SDL_GetError());
294 // set transparent color
295 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
296 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
298 SDL_SetWindowIcon(sdl_window, surface);
302 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
303 SDL_PixelFormat *format2)
305 return (format1->format == format2->format &&
306 format1->BitsPerPixel == format2->BitsPerPixel &&
307 format1->BytesPerPixel == format2->BytesPerPixel &&
308 format1->Rmask == format2->Rmask &&
309 format1->Gmask == format2->Gmask &&
310 format1->Bmask == format2->Bmask);
313 static void SDLCopyColorKey(SDL_Surface *src_surface, SDL_Surface *dst_surface)
318 // check if source surface has a color key
319 if (SDL_GetColorKey(src_surface, &color_key) == 0)
321 // get RGB values of color key of source surface
322 SDL_GetRGB(color_key, src_surface->format, &r, &g, &b);
324 // get color key from RGB values in destination surface format
325 color_key = SDL_MapRGB(dst_surface->format, r, g, b);
327 // set color key in destination surface
328 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL, color_key);
332 // unset color key in destination surface
333 SDL_SetColorKey(dst_surface, UNSET_TRANSPARENT_PIXEL, 0);
337 static boolean SDLHasColorKey(SDL_Surface *surface)
341 return (SDL_GetColorKey(surface, &color_key) == 0);
344 static boolean SDLHasAlpha(SDL_Surface *surface)
346 SDL_BlendMode blend_mode;
348 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
351 return (blend_mode == SDL_BLENDMODE_BLEND);
354 static void SDLSetSurfaceAlpha(SDL_Surface *surface, boolean set, int alpha)
356 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
358 SDL_SetSurfaceBlendMode(surface, blend_mode);
359 SDL_SetSurfaceAlphaMod(surface, alpha);
362 static void SDLSetTextureAlpha(SDL_Texture *texture, boolean set, int alpha)
364 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
366 SDL_SetTextureBlendMode(texture, blend_mode);
367 SDL_SetTextureAlphaMod(texture, alpha);
370 static void SDLSetBitmapAlpha(Bitmap *bitmap, boolean is_texture,
373 int alpha_next_blit = bitmap->alpha_next_blit;
375 // alpha value must be requested every time before blitting, if needed
376 bitmap->alpha_next_blit = -1;
378 // nothing to do if requested alpha value is already set
379 if (bitmap->alpha[is_texture][is_masked] == alpha_next_blit)
382 // store requested alpha value for masked/unmasked surface/texture
383 bitmap->alpha[is_texture][is_masked] = alpha_next_blit;
385 // set blend mode if bitmap is masked or if alpha value is defined
386 boolean set_blend_mode = (is_masked || alpha_next_blit != -1);
388 // if alpha value is undefined, use default (opaque) alpha value
389 if (alpha_next_blit == -1)
390 alpha_next_blit = SDL_ALPHA_OPAQUE;
393 SDLSetTextureAlpha(is_masked ? bitmap->texture_masked : bitmap->texture,
394 set_blend_mode, alpha_next_blit);
396 SDLSetSurfaceAlpha(is_masked ? bitmap->surface_masked : bitmap->surface,
397 set_blend_mode, alpha_next_blit);
400 void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
402 SDLSetSurfaceAlpha(surface, set, alpha);
405 const char *SDLGetRendererName(void)
407 static SDL_RendererInfo renderer_info;
409 SDL_GetRendererInfo(sdl_renderer, &renderer_info);
411 return renderer_info.name;
414 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
416 SDL_PixelFormat format;
417 SDL_Surface *new_surface;
422 if (backbuffer && backbuffer->surface)
424 format = *backbuffer->surface->format;
425 format.Amask = surface->format->Amask; // keep alpha channel
429 format = *surface->format;
432 new_surface = SDL_ConvertSurface(surface, &format, 0);
434 if (new_surface == NULL)
435 Fail("SDL_ConvertSurface() failed: %s", SDL_GetError());
437 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
438 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
439 SDLCopyColorKey(surface, new_surface);
444 boolean SDLSetNativeSurface(SDL_Surface **surface)
446 SDL_Surface *new_surface;
448 if (surface == NULL ||
450 backbuffer == NULL ||
451 backbuffer->surface == NULL)
454 // if pixel format already optimized for destination surface, do nothing
455 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
458 new_surface = SDLGetNativeSurface(*surface);
460 SDL_FreeSurface(*surface);
462 *surface = new_surface;
467 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
469 if (program.headless)
472 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
475 Fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
480 void SDLCreateBitmapTextures(Bitmap *bitmap)
486 SDL_DestroyTexture(bitmap->texture);
487 if (bitmap->texture_masked)
488 SDL_DestroyTexture(bitmap->texture_masked);
490 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
491 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
494 void SDLFreeBitmapTextures(Bitmap *bitmap)
500 SDL_DestroyTexture(bitmap->texture);
501 if (bitmap->texture_masked)
502 SDL_DestroyTexture(bitmap->texture_masked);
504 bitmap->texture = NULL;
505 bitmap->texture_masked = NULL;
508 void SDLInitVideoDisplay(void)
510 // set hint to select render driver as specified in setup config file
511 if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
512 SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
514 // initialize SDL video
515 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
516 Fail("SDL_InitSubSystem() failed: %s", SDL_GetError());
518 // set default SDL depth
519 video.default_depth = 32; // (how to determine video depth in SDL2?)
521 // Code used with SDL 1.2:
522 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
525 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
527 if (program.headless)
530 video.window_scaling_percent = setup.window_scaling_percent;
531 video.window_scaling_quality = setup.window_scaling_quality;
533 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
535 // SDL 2.0: support for (desktop) fullscreen mode available
536 video.fullscreen_available = TRUE;
538 // open SDL video output device (window or fullscreen mode)
539 if (!SDLSetVideoMode(fullscreen))
540 Fail("setting video mode failed");
542 // !!! SDL2 can only set the window icon if the window already exists !!!
544 SDLSetWindowIcon(program.icon_filename);
546 // set window and icon title
550 static void SDLInitVideoBuffer_DrawBuffer(void)
552 /* SDL cannot directly draw to the visible video framebuffer like X11,
553 but always uses a backbuffer, which is then blitted to the visible
554 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
555 visible video framebuffer with 'SDL_Flip', if the hardware supports
556 this). Therefore do not use an additional backbuffer for drawing, but
557 use a symbolic buffer (distinguishable from the SDL backbuffer) called
558 'window', which indicates that the SDL backbuffer should be updated to
559 the visible video framebuffer when attempting to blit to it.
561 For convenience, it seems to be a good idea to create this symbolic
562 buffer 'window' at the same size as the SDL backbuffer. Although it
563 should never be drawn to directly, it would do no harm nevertheless. */
565 // create additional (symbolic) buffer for double-buffering
566 ReCreateBitmap(&window, video.width, video.height);
568 // create dummy drawing buffer for headless mode, if needed
569 if (program.headless)
570 ReCreateBitmap(&backbuffer, video.width, video.height);
573 void SDLInitVideoBuffer(boolean fullscreen)
575 SDLInitVideoBuffer_VideoBuffer(fullscreen);
576 SDLInitVideoBuffer_DrawBuffer();
579 static boolean SDLCreateScreen(boolean fullscreen)
581 SDL_Surface *new_surface = NULL;
583 int surface_flags_window = SURFACE_FLAGS;
584 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
587 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
589 video.vsync_mode = VSYNC_MODE_OFF;
591 if (!strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF))
593 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
594 video.vsync_mode = VSYNC_MODE_NORMAL;
597 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
598 _without_ enabling 2D/3D acceleration and/or guest additions installed,
599 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
600 it will try to use accelerated graphics and apparently fails miserably) */
601 int renderer_flags = SDL_RENDERER_SOFTWARE;
604 int width = video.width;
605 int height = video.height;
606 int screen_width = video.screen_width;
607 int screen_height = video.screen_height;
608 int surface_flags = (fullscreen ? surface_flags_fullscreen :
609 surface_flags_window);
610 int display_nr = options.display_nr;
612 // default window size is unscaled
613 video.window_width = screen_width;
614 video.window_height = screen_height;
616 // store if initial screen mode is fullscreen mode when changing screen size
617 video.fullscreen_initial = fullscreen;
619 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
621 video.window_width = window_scaling_factor * screen_width;
622 video.window_height = window_scaling_factor * screen_height;
624 if (sdl_texture_stream)
626 SDL_DestroyTexture(sdl_texture_stream);
627 sdl_texture_stream = NULL;
630 if (sdl_texture_target)
632 SDL_DestroyTexture(sdl_texture_target);
633 sdl_texture_target = NULL;
636 if (!(fullscreen && fullscreen_enabled))
640 SDL_DestroyRenderer(sdl_renderer);
646 SDL_SetWindowSize(sdl_window, video.window_width, video.window_height);
650 if (sdl_window == NULL)
651 sdl_window = SDL_CreateWindow(program.window_title,
652 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
653 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
658 if (sdl_window != NULL)
660 if (sdl_renderer == NULL)
661 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
663 if (sdl_renderer != NULL)
665 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
666 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
668 // required for setting adaptive vsync when using OpenGL renderer
669 SDLSetScreenVsyncMode(setup.vsync_mode);
671 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
672 SDL_PIXELFORMAT_ARGB8888,
673 SDL_TEXTUREACCESS_STREAMING,
676 if (SDL_RenderTargetSupported(sdl_renderer))
677 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
678 SDL_PIXELFORMAT_ARGB8888,
679 SDL_TEXTUREACCESS_TARGET,
682 if (sdl_texture_stream != NULL)
684 // use SDL default values for RGB masks and no alpha channel
685 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
687 if (new_surface == NULL)
688 Warn("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
692 Warn("SDL_CreateTexture() failed: %s", SDL_GetError());
697 Warn("SDL_CreateRenderer() failed: %s", SDL_GetError());
702 Warn("SDL_CreateWindow() failed: %s", SDL_GetError());
705 SDLSetScreenProperties();
707 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
708 if (new_surface != NULL)
709 fullscreen_enabled = fullscreen;
711 if (backbuffer == NULL)
712 backbuffer = CreateBitmapStruct();
714 backbuffer->width = video.width;
715 backbuffer->height = video.height;
717 if (backbuffer->surface)
718 SDL_FreeSurface(backbuffer->surface);
720 backbuffer->surface = new_surface;
722 return (new_surface != NULL);
725 boolean SDLSetVideoMode(boolean fullscreen)
727 boolean success = FALSE;
731 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
733 // switch display to fullscreen mode, if available
734 success = SDLCreateScreen(TRUE);
738 // switching display to fullscreen mode failed -- do not try it again
739 video.fullscreen_available = FALSE;
743 video.fullscreen_enabled = TRUE;
747 if ((!fullscreen && video.fullscreen_enabled) || !success)
749 // switch display to window mode
750 success = SDLCreateScreen(FALSE);
754 // switching display to window mode failed -- should not happen
758 video.fullscreen_enabled = FALSE;
759 video.window_scaling_percent = setup.window_scaling_percent;
760 video.window_scaling_quality = setup.window_scaling_quality;
762 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
766 SDLRedrawWindow(); // map window
771 void SDLSetWindowTitle(void)
773 if (sdl_window == NULL)
776 SDL_SetWindowTitle(sdl_window, program.window_title);
779 void SDLSetWindowScaling(int window_scaling_percent)
781 if (sdl_window == NULL)
784 float window_scaling_factor = (float)window_scaling_percent / 100;
785 int new_window_width = (int)(window_scaling_factor * video.screen_width);
786 int new_window_height = (int)(window_scaling_factor * video.screen_height);
788 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
790 video.window_scaling_percent = window_scaling_percent;
791 video.window_width = new_window_width;
792 video.window_height = new_window_height;
797 void SDLSetWindowScalingQuality(char *window_scaling_quality)
799 SDL_Texture *new_texture;
801 if (sdl_texture_stream == NULL)
804 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
806 new_texture = SDL_CreateTexture(sdl_renderer,
807 SDL_PIXELFORMAT_ARGB8888,
808 SDL_TEXTUREACCESS_STREAMING,
809 video.width, video.height);
811 if (new_texture != NULL)
813 SDL_DestroyTexture(sdl_texture_stream);
815 sdl_texture_stream = new_texture;
818 if (SDL_RenderTargetSupported(sdl_renderer))
819 new_texture = SDL_CreateTexture(sdl_renderer,
820 SDL_PIXELFORMAT_ARGB8888,
821 SDL_TEXTUREACCESS_TARGET,
822 video.width, video.height);
826 if (new_texture != NULL)
828 SDL_DestroyTexture(sdl_texture_target);
830 sdl_texture_target = new_texture;
835 video.window_scaling_quality = window_scaling_quality;
838 void SDLSetWindowFullscreen(boolean fullscreen)
840 if (sdl_window == NULL)
843 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
845 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
846 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
848 // if screen size was changed in fullscreen mode, correct desktop window size
849 if (!fullscreen && video.fullscreen_initial)
851 SDLSetWindowScaling(setup.window_scaling_percent);
852 SDL_SetWindowPosition(sdl_window,
853 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr),
854 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr));
856 video.fullscreen_initial = FALSE;
860 void SDLSetDisplaySize(void)
862 if (sdl_renderer != NULL)
866 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
868 video.display_width = w;
869 video.display_height = h;
872 Debug("video", "SDL renderer size: %d x %d",
873 video.display_width, video.display_height);
878 SDL_Rect display_bounds;
880 SDL_GetDisplayBounds(0, &display_bounds);
882 video.display_width = display_bounds.w;
883 video.display_height = display_bounds.h;
886 Debug("video", "SDL display size: %d x %d",
887 video.display_width, video.display_height);
892 void SDLSetScreenSizeAndOffsets(int width, int height)
894 // set default video screen size and offsets
895 video.screen_width = width;
896 video.screen_height = height;
897 video.screen_xoffset = 0;
898 video.screen_yoffset = 0;
900 #if defined(USE_COMPLETE_DISPLAY)
901 float ratio_video = (float) width / height;
902 float ratio_display = (float) video.display_width / video.display_height;
904 if (ratio_video != ratio_display)
906 // adjust drawable screen size to cover the whole device display
908 if (ratio_video < ratio_display)
909 video.screen_width *= ratio_display / ratio_video;
911 video.screen_height *= ratio_video / ratio_display;
913 video.screen_xoffset = (video.screen_width - width) / 2;
914 video.screen_yoffset = (video.screen_height - height) / 2;
917 Debug("video", "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
919 video.screen_width, video.screen_height,
920 ratio_video, ratio_display);
926 void SDLSetScreenSizeForRenderer(int width, int height)
928 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
931 void SDLSetScreenProperties(void)
934 SDLSetScreenSizeAndOffsets(video.width, video.height);
935 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
937 SetOverlayGridSizeAndButtons();
940 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
942 video.screen_rendering_mode =
943 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
944 SPECIAL_RENDERING_BITMAP :
945 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
946 SPECIAL_RENDERING_TARGET:
947 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
948 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
951 void SDLSetScreenVsyncMode(char *vsync_mode)
953 // changing vsync mode without re-creating renderer only supported by OpenGL
954 if (!strPrefixLower((char *)SDLGetRendererName(), "opengl"))
957 int interval = VSYNC_MODE_STR_TO_INT(vsync_mode);
958 int result = SDL_GL_SetSwapInterval(interval);
960 // if adaptive vsync requested, but not supported, retry with normal vsync
961 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
963 interval = VSYNC_MODE_NORMAL;
965 result = SDL_GL_SetSwapInterval(interval);
969 interval = VSYNC_MODE_OFF;
971 video.vsync_mode = interval;
974 void SDLRedrawWindow(void)
976 UpdateScreen_WithoutFrameDelay(NULL);
979 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
982 if (program.headless)
985 SDL_Surface *surface =
986 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
989 Fail("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
991 SDLSetNativeSurface(&surface);
993 bitmap->surface = surface;
996 void SDLFreeBitmapPointers(Bitmap *bitmap)
999 SDL_FreeSurface(bitmap->surface);
1000 if (bitmap->surface_masked)
1001 SDL_FreeSurface(bitmap->surface_masked);
1003 bitmap->surface = NULL;
1004 bitmap->surface_masked = NULL;
1006 if (bitmap->texture)
1007 SDL_DestroyTexture(bitmap->texture);
1008 if (bitmap->texture_masked)
1009 SDL_DestroyTexture(bitmap->texture_masked);
1011 bitmap->texture = NULL;
1012 bitmap->texture_masked = NULL;
1015 void SDLBlitSurface(SDL_Surface *src_surface, SDL_Surface *dst_surface,
1016 int src_x, int src_y, int width, int height,
1017 int dst_x, int dst_y)
1019 SDL_Rect src_rect, dst_rect;
1024 src_rect.h = height;
1029 dst_rect.h = height;
1031 SDL_BlitSurface(src_surface, &src_rect, dst_surface, &dst_rect);
1034 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1035 int src_x, int src_y, int width, int height,
1036 int dst_x, int dst_y, int mask_mode)
1038 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1039 SDL_Rect src_rect, dst_rect;
1044 src_rect.h = height;
1049 dst_rect.h = height;
1051 SDLSetBitmapAlpha(src_bitmap, FALSE, mask_mode == BLIT_MASKED);
1053 // if (src_bitmap != backbuffer || dst_bitmap != window)
1054 if (!(src_bitmap == backbuffer && dst_bitmap == window))
1055 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
1056 src_bitmap->surface_masked : src_bitmap->surface),
1057 &src_rect, real_dst_bitmap->surface, &dst_rect);
1059 if (dst_bitmap == window)
1060 UpdateScreen_WithFrameDelay(&dst_rect);
1063 void SDLBlitTexture(Bitmap *bitmap,
1064 int src_x, int src_y, int width, int height,
1065 int dst_x, int dst_y, int mask_mode)
1067 SDL_Texture *texture;
1072 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1074 if (texture == NULL)
1080 src_rect.h = height;
1085 dst_rect.h = height;
1087 SDLSetBitmapAlpha(bitmap, TRUE, mask_mode == BLIT_MASKED);
1089 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1092 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1095 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1103 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1105 if (dst_bitmap == window)
1106 UpdateScreen_WithFrameDelay(&rect);
1109 void PrepareFadeBitmap(int draw_target)
1111 Bitmap *fade_bitmap =
1112 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1113 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1115 if (fade_bitmap == NULL)
1118 // copy backbuffer to fading buffer
1119 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1121 // add border and animations to fading buffer
1122 FinalizeScreen(draw_target);
1125 void SDLFadeRectangle(int x, int y, int width, int height,
1126 int fade_mode, int fade_delay, int post_delay,
1127 void (*draw_border_function)(void))
1129 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1130 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1131 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1132 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1133 SDL_Surface *surface_screen = backbuffer->surface;
1134 SDL_Rect src_rect, dst_rect;
1136 int src_x = x, src_y = y;
1137 int dst_x = x, dst_y = y;
1138 unsigned int time_last, time_current;
1140 // store function for drawing global masked border
1141 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1143 // deactivate drawing of global border while fading, if needed
1144 if (draw_border_function == NULL)
1145 gfx.draw_global_border_function = NULL;
1150 src_rect.h = height;
1154 dst_rect.w = width; // (ignored)
1155 dst_rect.h = height; // (ignored)
1157 dst_rect2 = dst_rect;
1159 // before fading in, store backbuffer (without animation graphics)
1160 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1161 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1163 // copy source and target surfaces to temporary surfaces for fading
1164 if (fade_mode & FADE_TYPE_TRANSFORM)
1166 // (source and target fading buffer already prepared)
1168 else if (fade_mode & FADE_TYPE_FADE_IN)
1170 // (target fading buffer already prepared)
1171 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1173 else // FADE_TYPE_FADE_OUT
1175 // (source fading buffer already prepared)
1176 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1179 time_current = SDL_GetTicks();
1181 if (fade_delay <= 0)
1183 // immediately draw final target frame without delay
1184 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1188 // when fading without delay, also skip post delay
1192 if (fade_mode == FADE_MODE_MELT)
1194 boolean done = FALSE;
1195 int melt_pixels = 2;
1196 int melt_columns = width / melt_pixels;
1197 int ypos[melt_columns];
1198 int max_steps = height / 8 + 32;
1203 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1205 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1207 ypos[0] = -GetSimpleRandom(16);
1209 for (i = 1 ; i < melt_columns; i++)
1211 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1213 ypos[i] = ypos[i - 1] + r;
1226 time_last = time_current;
1227 time_current = SDL_GetTicks();
1228 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1229 steps_final = MIN(MAX(0, steps), max_steps);
1233 done = (steps_done >= steps_final);
1235 for (i = 0 ; i < melt_columns; i++)
1243 else if (ypos[i] < height)
1248 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1250 if (ypos[i] + dy >= height)
1251 dy = height - ypos[i];
1253 // copy part of (appearing) target surface to upper area
1254 src_rect.x = src_x + i * melt_pixels;
1255 // src_rect.y = src_y + ypos[i];
1257 src_rect.w = melt_pixels;
1259 src_rect.h = ypos[i] + dy;
1261 dst_rect.x = dst_x + i * melt_pixels;
1262 // dst_rect.y = dst_y + ypos[i];
1265 if (steps_done >= steps_final)
1266 SDL_BlitSurface(surface_target, &src_rect,
1267 surface_screen, &dst_rect);
1271 // copy part of (disappearing) source surface to lower area
1272 src_rect.x = src_x + i * melt_pixels;
1274 src_rect.w = melt_pixels;
1275 src_rect.h = height - ypos[i];
1277 dst_rect.x = dst_x + i * melt_pixels;
1278 dst_rect.y = dst_y + ypos[i];
1280 if (steps_done >= steps_final)
1281 SDL_BlitSurface(surface_source, &src_rect,
1282 surface_screen, &dst_rect);
1288 src_rect.x = src_x + i * melt_pixels;
1290 src_rect.w = melt_pixels;
1291 src_rect.h = height;
1293 dst_rect.x = dst_x + i * melt_pixels;
1296 if (steps_done >= steps_final)
1297 SDL_BlitSurface(surface_target, &src_rect,
1298 surface_screen, &dst_rect);
1302 if (steps_done >= steps_final)
1304 if (draw_border_function != NULL)
1305 draw_border_function();
1307 UpdateScreen_WithFrameDelay(&dst_rect2);
1309 if (PendingEscapeKeyEvent())
1314 else if (fade_mode == FADE_MODE_CURTAIN)
1318 int xx_size = width / 2;
1320 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1322 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1324 for (xx = 0; xx < xx_size;)
1326 time_last = time_current;
1327 time_current = SDL_GetTicks();
1328 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1329 xx_final = MIN(MAX(0, xx), xx_size);
1334 src_rect.h = height;
1339 // draw new (target) image to screen buffer
1340 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1342 if (xx_final < xx_size)
1344 src_rect.w = xx_size - xx_final;
1345 src_rect.h = height;
1347 // draw old (source) image to screen buffer (left side)
1349 src_rect.x = src_x + xx_final;
1352 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1354 // draw old (source) image to screen buffer (right side)
1356 src_rect.x = src_x + xx_size;
1357 dst_rect.x = dst_x + xx_size + xx_final;
1359 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1362 if (draw_border_function != NULL)
1363 draw_border_function();
1365 // only update the region of the screen that is affected from fading
1366 UpdateScreen_WithFrameDelay(&dst_rect2);
1368 if (PendingEscapeKeyEvent())
1372 else // fading in, fading out or cross-fading
1377 for (alpha = 0.0; alpha < 255.0;)
1379 time_last = time_current;
1380 time_current = SDL_GetTicks();
1381 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1382 alpha_final = MIN(MAX(0, alpha), 255);
1384 // draw existing (source) image to screen buffer
1385 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1387 // draw new (target) image to screen buffer using alpha blending
1388 SDLSetAlpha(surface_target, TRUE, alpha_final);
1389 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1391 if (draw_border_function != NULL)
1392 draw_border_function();
1394 // only update the region of the screen that is affected from fading
1395 UpdateScreen_WithFrameDelay(&dst_rect);
1397 if (PendingEscapeKeyEvent())
1403 Delay_WithScreenUpdates(post_delay);
1405 // restore function for drawing global masked border
1406 gfx.draw_global_border_function = draw_global_border_function;
1408 // after fading in, restore backbuffer (without animation graphics)
1409 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1410 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1413 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1414 int to_x, int to_y, Uint32 color)
1416 SDL_Surface *surface = dst_bitmap->surface;
1420 swap_numbers(&from_x, &to_x);
1423 swap_numbers(&from_y, &to_y);
1427 rect.w = (to_x - from_x + 1);
1428 rect.h = (to_y - from_y + 1);
1430 SDL_FillRect(surface, &rect, color);
1433 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1434 int to_x, int to_y, Uint32 color)
1436 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1439 #if ENABLE_UNUSED_CODE
1440 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1441 int num_points, Uint32 color)
1446 for (i = 0; i < num_points - 1; i++)
1448 for (x = 0; x < line_width; x++)
1450 for (y = 0; y < line_width; y++)
1452 int dx = x - line_width / 2;
1453 int dy = y - line_width / 2;
1455 if ((x == 0 && y == 0) ||
1456 (x == 0 && y == line_width - 1) ||
1457 (x == line_width - 1 && y == 0) ||
1458 (x == line_width - 1 && y == line_width - 1))
1461 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1462 points[i+1].x + dx, points[i+1].y + dy, color);
1469 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1471 SDL_Surface *surface = src_bitmap->surface;
1473 switch (surface->format->BytesPerPixel)
1475 case 1: // assuming 8-bpp
1477 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1481 case 2: // probably 15-bpp or 16-bpp
1483 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1487 case 3: // slow 24-bpp mode; usually not used
1490 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1494 shift = surface->format->Rshift;
1495 color |= *(pix + shift / 8) >> shift;
1496 shift = surface->format->Gshift;
1497 color |= *(pix + shift / 8) >> shift;
1498 shift = surface->format->Bshift;
1499 color |= *(pix + shift / 8) >> shift;
1505 case 4: // probably 32-bpp
1507 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1516 // ============================================================================
1517 // The following functions were taken from the SGE library
1518 // (SDL Graphics Extension Library) by Anders Lindström
1519 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1520 // ============================================================================
1522 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1524 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1526 switch (surface->format->BytesPerPixel)
1531 *((Uint8 *)surface->pixels + y * surface->pitch + x) = color;
1537 // Probably 15-bpp or 16-bpp
1538 *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x) = color;
1544 // Slow 24-bpp mode, usually not used
1548 // Gack - slow, but endian correct
1549 pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1550 shift = surface->format->Rshift;
1551 *(pix + shift / 8) = color>>shift;
1552 shift = surface->format->Gshift;
1553 *(pix + shift / 8) = color>>shift;
1554 shift = surface->format->Bshift;
1555 *(pix + shift / 8) = color>>shift;
1562 *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x) = color;
1570 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1571 Uint8 R, Uint8 G, Uint8 B)
1573 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1576 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1578 *((Uint8 *)surface->pixels + y * surface->pitch + x) = color;
1581 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1583 *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x) = color;
1586 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1591 // Gack - slow, but endian correct
1592 pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1593 shift = surface->format->Rshift;
1594 *(pix + shift / 8) = color>>shift;
1595 shift = surface->format->Gshift;
1596 *(pix + shift / 8) = color>>shift;
1597 shift = surface->format->Bshift;
1598 *(pix + shift / 8) = color>>shift;
1601 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1603 *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x) = color;
1606 static void _PutPixelX(SDL_Surface *dest, Sint16 x, Sint16 y, Uint32 color)
1608 switch (dest->format->BytesPerPixel)
1611 *((Uint8 *)dest->pixels + y * dest->pitch + x) = color;
1615 *((Uint16 *)dest->pixels + y * dest->pitch / 2 + x) = color;
1619 _PutPixel24(dest, x, y, color);
1623 *((Uint32 *)dest->pixels + y * dest->pitch / 4 + x) = color;
1629 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1631 if (SDL_MUSTLOCK(surface))
1633 if (SDL_LockSurface(surface) < 0)
1639 _PutPixel(surface, x, y, color);
1641 if (SDL_MUSTLOCK(surface))
1643 SDL_UnlockSurface(surface);
1648 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1649 Uint8 r, Uint8 g, Uint8 b)
1651 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1654 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1656 if (y >= 0 && y <= dest->h - 1)
1658 switch (dest->format->BytesPerPixel)
1661 return y * dest->pitch;
1665 return y * dest->pitch / 2;
1669 return y * dest->pitch;
1673 return y * dest->pitch / 4;
1681 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1684 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1686 switch (surface->format->BytesPerPixel)
1691 *((Uint8 *)surface->pixels + ypitch + x) = color;
1697 // Probably 15-bpp or 16-bpp
1698 *((Uint16 *)surface->pixels + ypitch + x) = color;
1704 // Slow 24-bpp mode, usually not used
1708 // Gack - slow, but endian correct
1709 pix = (Uint8 *)surface->pixels + ypitch + x * 3;
1710 shift = surface->format->Rshift;
1711 *(pix + shift / 8) = color>>shift;
1712 shift = surface->format->Gshift;
1713 *(pix + shift / 8) = color>>shift;
1714 shift = surface->format->Bshift;
1715 *(pix + shift / 8) = color>>shift;
1722 *((Uint32 *)surface->pixels + ypitch + x) = color;
1729 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1734 if (SDL_MUSTLOCK(Surface))
1736 if (SDL_LockSurface(Surface) < 0)
1750 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1754 if (x2 > Surface->w - 1)
1755 x2 = Surface->w - 1;
1762 SDL_FillRect(Surface, &l, Color);
1764 if (SDL_MUSTLOCK(Surface))
1766 SDL_UnlockSurface(Surface);
1770 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1771 Uint8 R, Uint8 G, Uint8 B)
1773 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1776 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1789 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1793 if (x2 > Surface->w - 1)
1794 x2 = Surface->w - 1;
1801 SDL_FillRect(Surface, &l, Color);
1804 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1809 if (SDL_MUSTLOCK(Surface))
1811 if (SDL_LockSurface(Surface) < 0)
1825 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1829 if (y2 > Surface->h - 1)
1830 y2 = Surface->h - 1;
1837 SDL_FillRect(Surface, &l, Color);
1839 if (SDL_MUSTLOCK(Surface))
1841 SDL_UnlockSurface(Surface);
1845 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1846 Uint8 R, Uint8 G, Uint8 B)
1848 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1851 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1864 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1868 if (y2 > Surface->h - 1)
1869 y2 = Surface->h - 1;
1876 SDL_FillRect(Surface, &l, Color);
1880 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1881 Sint16 x2, Sint16 y2, Uint32 Color,
1882 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1885 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1890 sdx = (dx < 0) ? -1 : 1;
1891 sdy = (dy < 0) ? -1 : 1;
1903 for (x = 0; x < dx; x++)
1905 Callback(Surface, px, py, Color);
1919 for (y = 0; y < dy; y++)
1921 Callback(Surface, px, py, Color);
1936 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1937 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1938 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1941 sge_DoLine(Surface, X1, Y1, X2, Y2,
1942 SDL_MapRGB(Surface->format, R, G, B), Callback);
1946 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1949 if (SDL_MUSTLOCK(Surface))
1951 if (SDL_LockSurface(Surface) < 0)
1956 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1958 // unlock the display
1959 if (SDL_MUSTLOCK(Surface))
1961 SDL_UnlockSurface(Surface);
1966 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1967 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1969 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1973 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1975 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1979 // ----------------------------------------------------------------------------
1980 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1981 // ----------------------------------------------------------------------------
1983 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1984 int src_x, int src_y, int width, int height,
1985 int dst_x, int dst_y)
1989 for (y = 0; y < height; y++)
1991 for (x = 0; x < width; x++)
1993 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1995 if (pixel != BLACK_PIXEL)
1996 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
2002 // ============================================================================
2003 // The following functions were taken from the SDL_gfx library version 2.0.3
2004 // (Rotozoomer) by Andreas Schiffler
2005 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
2006 // ============================================================================
2008 // ----------------------------------------------------------------------------
2011 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
2012 // ----------------------------------------------------------------------------
2022 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
2025 tColorRGBA *sp, *csp, *dp;
2029 sp = csp = (tColorRGBA *) src->pixels;
2030 dp = (tColorRGBA *) dst->pixels;
2031 dgap = dst->pitch - dst->w * 4;
2033 for (y = 0; y < dst->h; y++)
2037 for (x = 0; x < dst->w; x++)
2039 tColorRGBA *sp0 = sp;
2040 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
2041 tColorRGBA *sp00 = &sp0[0];
2042 tColorRGBA *sp01 = &sp0[1];
2043 tColorRGBA *sp10 = &sp1[0];
2044 tColorRGBA *sp11 = &sp1[1];
2047 // create new color pixel from all four source color pixels
2048 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
2049 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
2050 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
2051 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
2056 // advance source pointers
2059 // advance destination pointer
2063 // advance source pointer
2064 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2066 // advance destination pointers
2067 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2073 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2075 int x, y, *sax, *say, *csax, *csay;
2077 tColorRGBA *sp, *csp, *csp0, *dp;
2080 // use specialized zoom function when scaling down to exactly half size
2081 if (src->w == 2 * dst->w &&
2082 src->h == 2 * dst->h)
2083 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2086 sx = (float) src->w / (float) dst->w;
2087 sy = (float) src->h / (float) dst->h;
2089 // allocate memory for row increments
2090 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2091 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2093 // precalculate row increments
2094 for (x = 0; x <= dst->w; x++)
2095 *csax++ = (int)(sx * x);
2097 for (y = 0; y <= dst->h; y++)
2098 *csay++ = (int)(sy * y);
2101 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2102 dp = (tColorRGBA *) dst->pixels;
2103 dgap = dst->pitch - dst->w * 4;
2106 for (y = 0; y < dst->h; y++)
2111 for (x = 0; x < dst->w; x++)
2116 // advance source pointers
2120 // advance destination pointer
2124 // advance source pointer
2126 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2128 // advance destination pointers
2129 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2138 // ----------------------------------------------------------------------------
2141 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2142 // ----------------------------------------------------------------------------
2144 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2146 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2147 Uint8 *sp, *dp, *csp;
2151 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2152 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2154 // allocate memory for row increments
2155 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2156 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2158 // precalculate row increments
2161 for (x = 0; x < dst->w; x++)
2164 *csax = (csx >> 16);
2171 for (y = 0; y < dst->h; y++)
2174 *csay = (csy >> 16);
2181 for (x = 0; x < dst->w; x++)
2189 for (y = 0; y < dst->h; y++)
2196 sp = csp = (Uint8 *) src->pixels;
2197 dp = (Uint8 *) dst->pixels;
2198 dgap = dst->pitch - dst->w;
2202 for (y = 0; y < dst->h; y++)
2206 for (x = 0; x < dst->w; x++)
2211 // advance source pointers
2215 // advance destination pointer
2219 // advance source pointer (for row)
2220 csp += ((*csay) * src->pitch);
2223 // advance destination pointers
2233 // ----------------------------------------------------------------------------
2236 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2237 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2238 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2239 // into a 32bit RGBA format on the fly.
2240 // ----------------------------------------------------------------------------
2242 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2244 SDL_Surface *zoom_src = NULL;
2245 SDL_Surface *zoom_dst = NULL;
2246 boolean is_converted = FALSE;
2253 // determine if source surface is 32 bit or 8 bit
2254 is_32bit = (src->format->BitsPerPixel == 32);
2256 if (is_32bit || src->format->BitsPerPixel == 8)
2258 // use source surface 'as is'
2263 // new source surface is 32 bit with a defined RGB ordering
2264 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2265 0x000000ff, 0x0000ff00, 0x00ff0000,
2266 (src->format->Amask ? 0xff000000 : 0));
2267 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2269 is_converted = TRUE;
2272 // allocate surface to completely contain the zoomed surface
2275 // target surface is 32 bit with source RGBA/ABGR ordering
2276 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2277 zoom_src->format->Rmask,
2278 zoom_src->format->Gmask,
2279 zoom_src->format->Bmask,
2280 zoom_src->format->Amask);
2284 // target surface is 8 bit
2285 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2289 // lock source surface
2290 SDL_LockSurface(zoom_src);
2292 // check which kind of surface we have
2295 // call the 32 bit transformation routine to do the zooming
2296 zoomSurfaceRGBA(zoom_src, zoom_dst);
2301 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2302 zoom_dst->format->palette->colors[i] =
2303 zoom_src->format->palette->colors[i];
2304 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2306 // call the 8 bit transformation routine to do the zooming
2307 zoomSurfaceY(zoom_src, zoom_dst);
2310 // unlock source surface
2311 SDL_UnlockSurface(zoom_src);
2313 // free temporary surface
2315 SDL_FreeSurface(zoom_src);
2317 // return destination surface
2321 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2323 SDL_Surface *new_surface;
2325 if (surface == NULL)
2328 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2329 Fail("SDLGetNativeSurface() failed");
2331 // remove alpha channel from native non-transparent surface, if defined
2332 SDLSetAlpha(new_surface, FALSE, 0);
2334 // remove transparent color from native non-transparent surface, if defined
2335 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2340 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2342 Bitmap *dst_bitmap = CreateBitmapStruct();
2343 SDL_Surface *src_surface = src_bitmap->surface_masked;
2344 SDL_Surface *dst_surface;
2346 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2347 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2349 dst_bitmap->width = dst_width;
2350 dst_bitmap->height = dst_height;
2352 // create zoomed temporary surface from source surface
2353 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2355 // create native format destination surface from zoomed temporary surface
2356 SDLSetNativeSurface(&dst_surface);
2358 // set color key for zoomed surface from source surface, if defined
2359 if (SDLHasColorKey(src_surface))
2360 SDLCopyColorKey(src_surface, dst_surface);
2362 // create native non-transparent surface for opaque blitting
2363 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2365 // set native transparent surface for masked blitting
2366 dst_bitmap->surface_masked = dst_surface;
2372 // ============================================================================
2373 // load image to bitmap
2374 // ============================================================================
2376 Bitmap *SDLLoadImage(char *filename)
2378 Bitmap *new_bitmap = CreateBitmapStruct();
2379 SDL_Surface *sdl_image_tmp;
2381 if (program.headless)
2383 // prevent sanity check warnings at later stage
2384 new_bitmap->width = new_bitmap->height = 1;
2389 print_timestamp_init("SDLLoadImage");
2391 print_timestamp_time(getBaseNamePtr(filename));
2393 // load image to temporary surface
2394 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2395 Fail("IMG_Load('%s') failed: %s", getBaseNamePtr(filename), SDL_GetError());
2397 print_timestamp_time("IMG_Load");
2399 UPDATE_BUSY_STATE();
2401 // create native non-transparent surface for current image
2402 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2403 Fail("SDLGetOpaqueSurface() failed");
2405 print_timestamp_time("SDLGetNativeSurface (opaque)");
2407 UPDATE_BUSY_STATE();
2409 // set black pixel to transparent if no alpha channel / transparent color
2410 if (!SDLHasAlpha(sdl_image_tmp) &&
2411 !SDLHasColorKey(sdl_image_tmp))
2412 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2413 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2415 // create native transparent surface for current image
2416 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2417 Fail("SDLGetNativeSurface() failed");
2419 print_timestamp_time("SDLGetNativeSurface (masked)");
2421 UPDATE_BUSY_STATE();
2423 // free temporary surface
2424 SDL_FreeSurface(sdl_image_tmp);
2426 new_bitmap->width = new_bitmap->surface->w;
2427 new_bitmap->height = new_bitmap->surface->h;
2429 print_timestamp_done("SDLLoadImage");
2435 // ----------------------------------------------------------------------------
2436 // custom cursor fuctions
2437 // ----------------------------------------------------------------------------
2439 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2441 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2442 cursor_info->width, cursor_info->height,
2443 cursor_info->hot_x, cursor_info->hot_y);
2446 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2448 static struct MouseCursorInfo *last_cursor_info = NULL;
2449 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2450 static SDL_Cursor *cursor_default = NULL;
2451 static SDL_Cursor *cursor_current = NULL;
2453 // if invoked for the first time, store the SDL default cursor
2454 if (cursor_default == NULL)
2455 cursor_default = SDL_GetCursor();
2457 // only create new cursor if cursor info (custom only) has changed
2458 if (cursor_info != NULL && cursor_info != last_cursor_info)
2460 cursor_current = create_cursor(cursor_info);
2461 last_cursor_info = cursor_info;
2464 // only set new cursor if cursor info (custom or NULL) has changed
2465 if (cursor_info != last_cursor_info2)
2466 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2468 last_cursor_info2 = cursor_info;
2472 // ============================================================================
2474 // ============================================================================
2476 void SDLOpenAudio(void)
2478 if (program.headless)
2481 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2483 Warn("SDL_InitSubSystem() failed: %s", SDL_GetError());
2488 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2489 AUDIO_NUM_CHANNELS_STEREO,
2490 setup.system.audio_fragment_size) < 0)
2492 Warn("Mix_OpenAudio() failed: %s", SDL_GetError());
2497 audio.sound_available = TRUE;
2498 audio.music_available = TRUE;
2499 audio.loops_available = TRUE;
2500 audio.sound_enabled = TRUE;
2502 // set number of available mixer channels
2503 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2504 audio.music_channel = MUSIC_CHANNEL;
2505 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2507 Mixer_InitChannels();
2510 void SDLCloseAudio(void)
2513 Mix_HaltChannel(-1);
2516 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2520 // ============================================================================
2522 // ============================================================================
2524 void SDLWaitEvent(Event *event)
2526 SDL_WaitEvent(event);
2529 void SDLCorrectRawMousePosition(int *x, int *y)
2531 if (sdl_renderer == NULL)
2534 // this corrects the raw mouse position for logical screen size within event
2535 // filters (correction done later by SDL library when handling mouse events)
2538 float scale_x, scale_y;
2540 SDL_RenderGetViewport(sdl_renderer, &viewport);
2541 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2543 *x = (int)(*x / scale_x);
2544 *y = (int)(*y / scale_y);
2551 // ============================================================================
2552 // joystick functions
2553 // ============================================================================
2555 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2556 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2557 static int sdl_js_axis[MAX_PLAYERS][2];
2558 static int sdl_js_button[MAX_PLAYERS][2];
2559 static boolean sdl_is_controller[MAX_PLAYERS];
2561 void SDLClearJoystickState(void)
2565 for (i = 0; i < MAX_PLAYERS; i++)
2567 for (j = 0; j < 2; j++)
2569 sdl_js_axis_raw[i][j] = -1;
2570 sdl_js_axis[i][j] = 0;
2571 sdl_js_button[i][j] = 0;
2576 boolean SDLOpenJoystick(int nr)
2578 if (nr < 0 || nr >= MAX_PLAYERS)
2581 sdl_is_controller[nr] = SDL_IsGameController(nr);
2584 Debug("joystick", "opening joystick %d (%s)",
2585 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2588 if (sdl_is_controller[nr])
2589 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2591 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2593 return (sdl_joystick[nr] != NULL);
2596 void SDLCloseJoystick(int nr)
2598 if (nr < 0 || nr >= MAX_PLAYERS)
2602 Debug("joystick", "closing joystick %d", nr);
2605 if (sdl_is_controller[nr])
2606 SDL_GameControllerClose(sdl_joystick[nr]);
2608 SDL_JoystickClose(sdl_joystick[nr]);
2610 sdl_joystick[nr] = NULL;
2613 boolean SDLCheckJoystickOpened(int nr)
2615 if (nr < 0 || nr >= MAX_PLAYERS)
2618 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2621 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2623 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2624 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2625 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2626 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2628 if (nr < 0 || nr >= MAX_PLAYERS)
2634 // prevent (slightly jittering, but centered) axis A from resetting axis B
2635 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2636 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2639 sdl_js_axis[nr][axis_id] = axis_value;
2640 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2643 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2645 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2646 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2647 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2648 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2649 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2650 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2651 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2652 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2655 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2656 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2657 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2658 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2659 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2660 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2661 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2662 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2664 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2665 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2666 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2667 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2668 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2670 if (nr < 0 || nr >= MAX_PLAYERS)
2673 if (button_id == -1)
2676 sdl_js_button[nr][button_id] = button_state;
2679 void HandleJoystickEvent(Event *event)
2681 // when using joystick, disable overlay touch buttons
2682 runtime.uses_touch_device = FALSE;
2684 switch (event->type)
2686 case SDL_CONTROLLERDEVICEADDED:
2688 Debug("joystick", "SDL_CONTROLLERDEVICEADDED: device %d added",
2689 event->cdevice.which);
2694 case SDL_CONTROLLERDEVICEREMOVED:
2696 Debug("joystick", "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2697 event->cdevice.which);
2702 case SDL_CONTROLLERAXISMOTION:
2704 Debug("joystick", "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2705 event->caxis.which, event->caxis.axis, event->caxis.value);
2707 setJoystickAxis(event->caxis.which,
2709 event->caxis.value);
2712 case SDL_CONTROLLERBUTTONDOWN:
2714 Debug("joystick", "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2715 event->cbutton.which, event->cbutton.button);
2717 setJoystickButton(event->cbutton.which,
2718 event->cbutton.button,
2722 case SDL_CONTROLLERBUTTONUP:
2724 Debug("joystick", "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2725 event->cbutton.which, event->cbutton.button);
2727 setJoystickButton(event->cbutton.which,
2728 event->cbutton.button,
2732 case SDL_JOYAXISMOTION:
2733 if (sdl_is_controller[event->jaxis.which])
2737 Debug("joystick", "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2738 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2740 if (event->jaxis.axis < 4)
2741 setJoystickAxis(event->jaxis.which,
2743 event->jaxis.value);
2746 case SDL_JOYBUTTONDOWN:
2747 if (sdl_is_controller[event->jaxis.which])
2751 Debug("joystick", "SDL_JOYBUTTONDOWN: device %d, button %d",
2752 event->jbutton.which, event->jbutton.button);
2754 if (event->jbutton.button < 4)
2755 setJoystickButton(event->jbutton.which,
2756 event->jbutton.button,
2760 case SDL_JOYBUTTONUP:
2761 if (sdl_is_controller[event->jaxis.which])
2765 Debug("joystick", "SDL_JOYBUTTONUP: device %d, button %d",
2766 event->jbutton.which, event->jbutton.button);
2768 if (event->jbutton.button < 4)
2769 setJoystickButton(event->jbutton.which,
2770 event->jbutton.button,
2779 void SDLInitJoysticks(void)
2781 static boolean sdl_joystick_subsystem_initialized = FALSE;
2782 boolean print_warning = !sdl_joystick_subsystem_initialized;
2783 char *mappings_file_base = getPath2(options.conf_directory,
2784 GAMECONTROLLER_BASENAME);
2785 char *mappings_file_user = getPath2(getMainUserGameDataDir(),
2786 GAMECONTROLLER_BASENAME);
2790 if (!sdl_joystick_subsystem_initialized)
2792 sdl_joystick_subsystem_initialized = TRUE;
2794 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2796 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2797 Fail("SDL_Init() failed: %s", SDL_GetError());
2799 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2801 // the included game controller base mappings should always be found
2802 if (num_mappings == -1)
2803 Warn("no game controller base mappings found");
2806 Debug("joystick", "%d game controller base mapping(s) added",
2810 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2813 // the personal game controller user mappings may or may not be found
2814 if (num_mappings == -1)
2815 Warn("no game controller user mappings found");
2817 Debug("joystick", , "%d game controller user mapping(s) added",
2820 Debug("joystick", "%d joystick(s) found:", SDL_NumJoysticks());
2823 checked_free(mappings_file_base);
2824 checked_free(mappings_file_user);
2827 for (i = 0; i < SDL_NumJoysticks(); i++)
2829 const char *name, *type;
2831 if (SDL_IsGameController(i))
2833 name = SDL_GameControllerNameForIndex(i);
2834 type = "game controller";
2838 name = SDL_JoystickNameForIndex(i);
2842 Debug("joystick", "- joystick %d (%s): '%s'",
2843 i, type, (name ? name : "(Unknown)"));
2848 // assign joysticks from configured to connected joystick for all players
2849 for (i = 0; i < MAX_PLAYERS; i++)
2851 // get configured joystick for this player
2852 char *device_name = setup.input[i].joy.device_name;
2853 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2855 if (joystick_nr >= SDL_NumJoysticks())
2857 if (setup.input[i].use_joystick && print_warning)
2858 Warn("cannot find joystick %d", joystick_nr);
2863 // store configured joystick number for each player
2864 joystick.nr[i] = joystick_nr;
2867 // now open all connected joysticks (regardless if configured or not)
2868 for (i = 0; i < SDL_NumJoysticks(); i++)
2870 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2871 if (SDLCheckJoystickOpened(i))
2872 SDLCloseJoystick(i);
2874 if (SDLOpenJoystick(i))
2875 joystick.status = JOYSTICK_ACTIVATED;
2876 else if (print_warning)
2877 Warn("cannot open joystick %d", i);
2880 SDLClearJoystickState();
2883 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2885 if (nr < 0 || nr >= MAX_PLAYERS)
2889 *x = sdl_js_axis[nr][0];
2891 *y = sdl_js_axis[nr][1];
2894 *b1 = sdl_js_button[nr][0];
2896 *b2 = sdl_js_button[nr][1];
2902 // ============================================================================
2903 // touch input overlay functions
2904 // ============================================================================
2906 #if defined(USE_TOUCH_INPUT_OVERLAY)
2907 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2910 int grid_xsize = overlay.grid_xsize;
2911 int grid_ysize = overlay.grid_ysize;
2914 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2915 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2917 for (x = 0; x < grid_xsize; x++)
2919 rect.x = (x + 0) * video.screen_width / grid_xsize;
2920 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2922 for (y = 0; y < grid_ysize; y++)
2924 rect.y = (y + 0) * video.screen_height / grid_ysize;
2925 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2927 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2928 SDL_RenderDrawRect(sdl_renderer, &rect);
2932 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2935 static void RenderFillRectangle(int x, int y, int width, int height)
2937 SDL_Rect rect = { x, y, width, height };
2939 SDL_RenderFillRect(sdl_renderer, &rect);
2942 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2944 static int alpha_direction = 0;
2945 static int alpha_highlight = 0;
2946 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2947 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2949 int grid_xsize = overlay.grid_xsize;
2950 int grid_ysize = overlay.grid_ysize;
2953 if (alpha == alpha_max)
2955 if (alpha_direction < 0)
2957 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2959 if (alpha_highlight == 0)
2960 alpha_direction = 1;
2964 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2966 if (alpha_highlight == alpha_max)
2967 alpha_direction = -1;
2972 alpha_direction = 1;
2973 alpha_highlight = alpha;
2976 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2978 for (x = 0; x < grid_xsize; x++)
2980 for (y = 0; y < grid_ysize; y++)
2982 int grid_button = overlay.grid_button[x][y];
2983 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2984 int alpha_draw = alpha;
2985 int outline_border = MV_NONE;
2986 int border_size = 2;
2987 boolean draw_outlined = setup.touch.draw_outlined;
2988 boolean draw_pressed = setup.touch.draw_pressed;
2990 if (grid_button == CHAR_GRID_BUTTON_NONE)
2993 if (grid_button == overlay.grid_button_highlight)
2995 draw_outlined = FALSE;
2996 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2999 if (draw_pressed && overlay.grid_button_action & grid_button_action)
3002 draw_outlined = FALSE;
3004 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
3007 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
3009 rect.x = (x + 0) * video.screen_width / grid_xsize;
3010 rect.y = (y + 0) * video.screen_height / grid_ysize;
3011 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
3012 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
3014 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
3016 rect.x += border_size;
3017 rect.w -= border_size;
3019 outline_border |= MV_LEFT;
3022 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
3024 rect.w -= border_size;
3026 outline_border |= MV_RIGHT;
3029 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
3031 rect.y += border_size;
3032 rect.h -= border_size;
3034 outline_border |= MV_UP;
3037 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
3039 rect.h -= border_size;
3041 outline_border |= MV_DOWN;
3046 int rect_x = rect.x +
3047 (outline_border & MV_LEFT ? border_size : 0);
3048 int rect_w = rect.w -
3049 (outline_border & MV_LEFT ? border_size : 0) -
3050 (outline_border & MV_RIGHT ? border_size : 0);
3052 if (outline_border & MV_LEFT)
3053 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
3055 if (outline_border & MV_RIGHT)
3056 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
3057 border_size, rect.h);
3059 if (outline_border & MV_UP)
3060 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
3062 if (outline_border & MV_DOWN)
3063 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
3064 rect_w, border_size);
3068 SDL_RenderFillRect(sdl_renderer, &rect);
3073 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3076 static void DrawTouchInputOverlay(void)
3078 static boolean deactivated = TRUE;
3079 static boolean show_grid = FALSE;
3080 static int alpha = 0;
3081 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3082 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3083 boolean active = (overlay.enabled && overlay.active);
3085 if (!active && deactivated)
3090 if (alpha < alpha_max)
3091 alpha = MIN(alpha + alpha_step, alpha_max);
3093 deactivated = FALSE;
3097 alpha = MAX(0, alpha - alpha_step);
3103 if (overlay.show_grid)
3105 else if (deactivated)
3109 DrawTouchInputOverlay_ShowGrid(alpha);
3111 DrawTouchInputOverlay_ShowGridButtons(alpha);
3114 static void DrawTouchGadgetsOverlay(void)
3116 DrawGadgets_OverlayTouchButtons();