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 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
420 SDL_PixelFormat format;
421 SDL_Surface *new_surface;
426 if (backbuffer && backbuffer->surface)
428 format = *backbuffer->surface->format;
429 format.Amask = surface->format->Amask; // keep alpha channel
433 format = *surface->format;
436 new_surface = SDL_ConvertSurface(surface, &format, 0);
438 if (new_surface == NULL)
439 Fail("SDL_ConvertSurface() failed: %s", SDL_GetError());
441 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
442 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
443 SDLCopyColorKey(surface, new_surface);
448 boolean SDLSetNativeSurface(SDL_Surface **surface)
450 SDL_Surface *new_surface;
452 if (surface == NULL ||
454 backbuffer == NULL ||
455 backbuffer->surface == NULL)
458 // if pixel format already optimized for destination surface, do nothing
459 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
462 new_surface = SDLGetNativeSurface(*surface);
464 SDL_FreeSurface(*surface);
466 *surface = new_surface;
471 SDL_Surface *SDLCreateNativeSurface(int width, int height, int depth)
473 if (program.headless)
476 SDL_Surface *surface = SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
479 Fail("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
481 SDLSetNativeSurface(&surface);
486 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
488 if (program.headless)
491 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
494 Fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
499 void SDLCreateBitmapTextures(Bitmap *bitmap)
505 SDL_DestroyTexture(bitmap->texture);
506 if (bitmap->texture_masked)
507 SDL_DestroyTexture(bitmap->texture_masked);
509 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
510 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
513 void SDLFreeBitmapTextures(Bitmap *bitmap)
519 SDL_DestroyTexture(bitmap->texture);
520 if (bitmap->texture_masked)
521 SDL_DestroyTexture(bitmap->texture_masked);
523 bitmap->texture = NULL;
524 bitmap->texture_masked = NULL;
527 void SDLInitVideoDisplay(void)
529 // set hint to select render driver as specified in setup config file
530 if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
531 SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
533 // initialize SDL video
534 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
535 Fail("SDL_InitSubSystem() failed: %s", SDL_GetError());
537 // set default SDL depth
538 video.default_depth = 32; // (how to determine video depth in SDL2?)
540 // Code used with SDL 1.2:
541 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
544 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
546 if (program.headless)
549 video.window_scaling_percent = setup.window_scaling_percent;
550 video.window_scaling_quality = setup.window_scaling_quality;
552 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
554 // SDL 2.0: support for (desktop) fullscreen mode available
555 video.fullscreen_available = TRUE;
557 // open SDL video output device (window or fullscreen mode)
558 if (!SDLSetVideoMode(fullscreen))
559 Fail("setting video mode failed");
561 // !!! SDL2 can only set the window icon if the window already exists !!!
563 SDLSetWindowIcon(program.icon_filename);
565 // set window and icon title
569 static void SDLInitVideoBuffer_DrawBuffer(void)
571 /* SDL cannot directly draw to the visible video framebuffer like X11,
572 but always uses a backbuffer, which is then blitted to the visible
573 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
574 visible video framebuffer with 'SDL_Flip', if the hardware supports
575 this). Therefore do not use an additional backbuffer for drawing, but
576 use a symbolic buffer (distinguishable from the SDL backbuffer) called
577 'window', which indicates that the SDL backbuffer should be updated to
578 the visible video framebuffer when attempting to blit to it.
580 For convenience, it seems to be a good idea to create this symbolic
581 buffer 'window' at the same size as the SDL backbuffer. Although it
582 should never be drawn to directly, it would do no harm nevertheless. */
584 // create additional (symbolic) buffer for double-buffering
585 ReCreateBitmap(&window, video.width, video.height);
587 // create dummy drawing buffer for headless mode, if needed
588 if (program.headless)
589 ReCreateBitmap(&backbuffer, video.width, video.height);
592 void SDLInitVideoBuffer(boolean fullscreen)
594 SDLInitVideoBuffer_VideoBuffer(fullscreen);
595 SDLInitVideoBuffer_DrawBuffer();
598 static boolean SDLCreateScreen(boolean fullscreen)
600 SDL_Surface *new_surface = NULL;
602 int surface_flags_window = SURFACE_FLAGS;
603 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
606 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
608 video.vsync_mode = VSYNC_MODE_OFF;
610 if (!strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF))
612 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
613 video.vsync_mode = VSYNC_MODE_NORMAL;
616 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
617 _without_ enabling 2D/3D acceleration and/or guest additions installed,
618 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
619 it will try to use accelerated graphics and apparently fails miserably) */
620 int renderer_flags = SDL_RENDERER_SOFTWARE;
623 int width = video.width;
624 int height = video.height;
625 int screen_width = video.screen_width;
626 int screen_height = video.screen_height;
627 int surface_flags = (fullscreen ? surface_flags_fullscreen :
628 surface_flags_window);
629 int display_nr = options.display_nr;
631 // default window size is unscaled
632 video.window_width = screen_width;
633 video.window_height = screen_height;
635 // store if initial screen mode is fullscreen mode when changing screen size
636 video.fullscreen_initial = fullscreen;
638 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
640 video.window_width = window_scaling_factor * screen_width;
641 video.window_height = window_scaling_factor * screen_height;
643 if (sdl_texture_stream)
645 SDL_DestroyTexture(sdl_texture_stream);
646 sdl_texture_stream = NULL;
649 if (sdl_texture_target)
651 SDL_DestroyTexture(sdl_texture_target);
652 sdl_texture_target = NULL;
655 if (!(fullscreen && fullscreen_enabled))
659 SDL_DestroyRenderer(sdl_renderer);
665 SDL_SetWindowSize(sdl_window, video.window_width, video.window_height);
669 if (sdl_window == NULL)
670 sdl_window = SDL_CreateWindow(program.window_title,
671 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
672 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
677 if (sdl_window != NULL)
679 if (sdl_renderer == NULL)
680 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
682 if (sdl_renderer != NULL)
684 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
685 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
687 // required for setting adaptive vsync when using OpenGL renderer
688 SDLSetScreenVsyncMode(setup.vsync_mode);
690 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
691 SDL_PIXELFORMAT_ARGB8888,
692 SDL_TEXTUREACCESS_STREAMING,
695 if (SDL_RenderTargetSupported(sdl_renderer))
696 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
697 SDL_PIXELFORMAT_ARGB8888,
698 SDL_TEXTUREACCESS_TARGET,
701 if (sdl_texture_stream != NULL)
703 // use SDL default values for RGB masks and no alpha channel
704 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
706 if (new_surface == NULL)
707 Warn("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
711 Warn("SDL_CreateTexture() failed: %s", SDL_GetError());
716 Warn("SDL_CreateRenderer() failed: %s", SDL_GetError());
721 Warn("SDL_CreateWindow() failed: %s", SDL_GetError());
724 SDLSetScreenProperties();
726 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
727 if (new_surface != NULL)
728 fullscreen_enabled = fullscreen;
730 if (backbuffer == NULL)
731 backbuffer = CreateBitmapStruct();
733 backbuffer->width = video.width;
734 backbuffer->height = video.height;
736 if (backbuffer->surface)
737 SDL_FreeSurface(backbuffer->surface);
739 backbuffer->surface = new_surface;
741 return (new_surface != NULL);
744 boolean SDLSetVideoMode(boolean fullscreen)
746 boolean success = FALSE;
750 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
752 // switch display to fullscreen mode, if available
753 success = SDLCreateScreen(TRUE);
757 // switching display to fullscreen mode failed -- do not try it again
758 video.fullscreen_available = FALSE;
762 video.fullscreen_enabled = TRUE;
766 if ((!fullscreen && video.fullscreen_enabled) || !success)
768 // switch display to window mode
769 success = SDLCreateScreen(FALSE);
773 // switching display to window mode failed -- should not happen
777 video.fullscreen_enabled = FALSE;
778 video.window_scaling_percent = setup.window_scaling_percent;
779 video.window_scaling_quality = setup.window_scaling_quality;
781 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
785 SDLRedrawWindow(); // map window
790 void SDLSetWindowTitle(void)
792 if (sdl_window == NULL)
795 SDL_SetWindowTitle(sdl_window, program.window_title);
798 void SDLSetWindowScaling(int window_scaling_percent)
800 if (sdl_window == NULL)
803 float window_scaling_factor = (float)window_scaling_percent / 100;
804 int new_window_width = (int)(window_scaling_factor * video.screen_width);
805 int new_window_height = (int)(window_scaling_factor * video.screen_height);
807 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
809 video.window_scaling_percent = window_scaling_percent;
810 video.window_width = new_window_width;
811 video.window_height = new_window_height;
816 void SDLSetWindowScalingQuality(char *window_scaling_quality)
818 SDL_Texture *new_texture;
820 if (sdl_texture_stream == NULL)
823 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
825 new_texture = SDL_CreateTexture(sdl_renderer,
826 SDL_PIXELFORMAT_ARGB8888,
827 SDL_TEXTUREACCESS_STREAMING,
828 video.width, video.height);
830 if (new_texture != NULL)
832 SDL_DestroyTexture(sdl_texture_stream);
834 sdl_texture_stream = new_texture;
837 if (SDL_RenderTargetSupported(sdl_renderer))
838 new_texture = SDL_CreateTexture(sdl_renderer,
839 SDL_PIXELFORMAT_ARGB8888,
840 SDL_TEXTUREACCESS_TARGET,
841 video.width, video.height);
845 if (new_texture != NULL)
847 SDL_DestroyTexture(sdl_texture_target);
849 sdl_texture_target = new_texture;
854 video.window_scaling_quality = window_scaling_quality;
857 void SDLSetWindowFullscreen(boolean fullscreen)
859 if (sdl_window == NULL)
862 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
864 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
865 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
867 // if screen size was changed in fullscreen mode, correct desktop window size
868 if (!fullscreen && video.fullscreen_initial)
870 SDLSetWindowScaling(setup.window_scaling_percent);
871 SDL_SetWindowPosition(sdl_window,
872 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr),
873 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr));
875 video.fullscreen_initial = FALSE;
879 void SDLSetDisplaySize(void)
881 if (sdl_renderer != NULL)
885 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
887 video.display_width = w;
888 video.display_height = h;
891 Debug("video", "SDL renderer size: %d x %d",
892 video.display_width, video.display_height);
897 SDL_Rect display_bounds;
899 SDL_GetDisplayBounds(0, &display_bounds);
901 video.display_width = display_bounds.w;
902 video.display_height = display_bounds.h;
905 Debug("video", "SDL display size: %d x %d",
906 video.display_width, video.display_height);
911 void SDLSetScreenSizeAndOffsets(int width, int height)
913 // set default video screen size and offsets
914 video.screen_width = width;
915 video.screen_height = height;
916 video.screen_xoffset = 0;
917 video.screen_yoffset = 0;
919 #if defined(USE_COMPLETE_DISPLAY)
920 float ratio_video = (float) width / height;
921 float ratio_display = (float) video.display_width / video.display_height;
923 if (ratio_video != ratio_display)
925 // adjust drawable screen size to cover the whole device display
927 if (ratio_video < ratio_display)
928 video.screen_width *= ratio_display / ratio_video;
930 video.screen_height *= ratio_video / ratio_display;
932 video.screen_xoffset = (video.screen_width - width) / 2;
933 video.screen_yoffset = (video.screen_height - height) / 2;
936 Debug("video", "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
938 video.screen_width, video.screen_height,
939 ratio_video, ratio_display);
945 void SDLSetScreenSizeForRenderer(int width, int height)
947 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
950 void SDLSetScreenProperties(void)
953 SDLSetScreenSizeAndOffsets(video.width, video.height);
954 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
956 SetOverlayGridSizeAndButtons();
959 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
961 video.screen_rendering_mode =
962 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
963 SPECIAL_RENDERING_BITMAP :
964 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
965 SPECIAL_RENDERING_TARGET:
966 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
967 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
970 void SDLSetScreenVsyncMode(char *vsync_mode)
972 // changing vsync mode without re-creating renderer only supported by OpenGL
973 if (!strPrefixLower((char *)SDLGetRendererName(), "opengl"))
976 int interval = VSYNC_MODE_STR_TO_INT(vsync_mode);
977 int result = SDL_GL_SetSwapInterval(interval);
979 // if adaptive vsync requested, but not supported, retry with normal vsync
980 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
982 interval = VSYNC_MODE_NORMAL;
984 result = SDL_GL_SetSwapInterval(interval);
988 interval = VSYNC_MODE_OFF;
990 video.vsync_mode = interval;
993 void SDLRedrawWindow(void)
995 UpdateScreen_WithoutFrameDelay(NULL);
998 void SDLFreeBitmapPointers(Bitmap *bitmap)
1000 if (bitmap->surface)
1001 SDL_FreeSurface(bitmap->surface);
1002 if (bitmap->surface_masked)
1003 SDL_FreeSurface(bitmap->surface_masked);
1005 bitmap->surface = NULL;
1006 bitmap->surface_masked = NULL;
1008 if (bitmap->texture)
1009 SDL_DestroyTexture(bitmap->texture);
1010 if (bitmap->texture_masked)
1011 SDL_DestroyTexture(bitmap->texture_masked);
1013 bitmap->texture = NULL;
1014 bitmap->texture_masked = NULL;
1017 void SDLBlitSurface(SDL_Surface *src_surface, SDL_Surface *dst_surface,
1018 int src_x, int src_y, int width, int height,
1019 int dst_x, int dst_y)
1021 SDL_Rect src_rect, dst_rect;
1026 src_rect.h = height;
1031 dst_rect.h = height;
1033 SDL_BlitSurface(src_surface, &src_rect, dst_surface, &dst_rect);
1036 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1037 int src_x, int src_y, int width, int height,
1038 int dst_x, int dst_y, int mask_mode)
1040 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1041 SDL_Rect src_rect, dst_rect;
1046 src_rect.h = height;
1051 dst_rect.h = height;
1053 SDLSetBitmapAlpha(src_bitmap, FALSE, mask_mode == BLIT_MASKED);
1055 // if (src_bitmap != backbuffer || dst_bitmap != window)
1056 if (!(src_bitmap == backbuffer && dst_bitmap == window))
1057 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
1058 src_bitmap->surface_masked : src_bitmap->surface),
1059 &src_rect, real_dst_bitmap->surface, &dst_rect);
1061 if (dst_bitmap == window)
1062 UpdateScreen_WithFrameDelay(&dst_rect);
1065 void SDLBlitTexture(Bitmap *bitmap,
1066 int src_x, int src_y, int width, int height,
1067 int dst_x, int dst_y, int mask_mode)
1069 SDL_Texture *texture;
1074 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1076 if (texture == NULL)
1082 src_rect.h = height;
1087 dst_rect.h = height;
1089 SDLSetBitmapAlpha(bitmap, TRUE, mask_mode == BLIT_MASKED);
1091 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1094 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1097 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1105 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1107 if (dst_bitmap == window)
1108 UpdateScreen_WithFrameDelay(&rect);
1111 void PrepareFadeBitmap(int draw_target)
1113 Bitmap *fade_bitmap =
1114 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1115 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1117 if (fade_bitmap == NULL)
1120 // copy backbuffer to fading buffer
1121 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1123 // add border and animations to fading buffer
1124 FinalizeScreen(draw_target);
1127 void SDLFadeRectangle(int x, int y, int width, int height,
1128 int fade_mode, int fade_delay, int post_delay,
1129 void (*draw_border_function)(void))
1131 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1132 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1133 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1134 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1135 SDL_Surface *surface_screen = backbuffer->surface;
1136 SDL_Rect src_rect, dst_rect;
1138 int src_x = x, src_y = y;
1139 int dst_x = x, dst_y = y;
1140 unsigned int time_last, time_current;
1142 // store function for drawing global masked border
1143 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1145 // deactivate drawing of global border while fading, if needed
1146 if (draw_border_function == NULL)
1147 gfx.draw_global_border_function = NULL;
1152 src_rect.h = height;
1156 dst_rect.w = width; // (ignored)
1157 dst_rect.h = height; // (ignored)
1159 dst_rect2 = dst_rect;
1161 // before fading in, store backbuffer (without animation graphics)
1162 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1163 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1165 // copy source and target surfaces to temporary surfaces for fading
1166 if (fade_mode & FADE_TYPE_TRANSFORM)
1168 // (source and target fading buffer already prepared)
1170 else if (fade_mode & FADE_TYPE_FADE_IN)
1172 // (target fading buffer already prepared)
1173 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1175 else // FADE_TYPE_FADE_OUT
1177 // (source fading buffer already prepared)
1178 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1181 time_current = SDL_GetTicks();
1183 if (fade_delay <= 0)
1185 // immediately draw final target frame without delay
1186 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1190 // when fading without delay, also skip post delay
1194 if (fade_mode == FADE_MODE_MELT)
1196 boolean done = FALSE;
1197 int melt_pixels = 2;
1198 int melt_columns = width / melt_pixels;
1199 int ypos[melt_columns];
1200 int max_steps = height / 8 + 32;
1205 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1207 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1209 ypos[0] = -GetSimpleRandom(16);
1211 for (i = 1 ; i < melt_columns; i++)
1213 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1215 ypos[i] = ypos[i - 1] + r;
1228 time_last = time_current;
1229 time_current = SDL_GetTicks();
1230 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1231 steps_final = MIN(MAX(0, steps), max_steps);
1235 done = (steps_done >= steps_final);
1237 for (i = 0 ; i < melt_columns; i++)
1245 else if (ypos[i] < height)
1250 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1252 if (ypos[i] + dy >= height)
1253 dy = height - ypos[i];
1255 // copy part of (appearing) target surface to upper area
1256 src_rect.x = src_x + i * melt_pixels;
1257 // src_rect.y = src_y + ypos[i];
1259 src_rect.w = melt_pixels;
1261 src_rect.h = ypos[i] + dy;
1263 dst_rect.x = dst_x + i * melt_pixels;
1264 // dst_rect.y = dst_y + ypos[i];
1267 if (steps_done >= steps_final)
1268 SDL_BlitSurface(surface_target, &src_rect,
1269 surface_screen, &dst_rect);
1273 // copy part of (disappearing) source surface to lower area
1274 src_rect.x = src_x + i * melt_pixels;
1276 src_rect.w = melt_pixels;
1277 src_rect.h = height - ypos[i];
1279 dst_rect.x = dst_x + i * melt_pixels;
1280 dst_rect.y = dst_y + ypos[i];
1282 if (steps_done >= steps_final)
1283 SDL_BlitSurface(surface_source, &src_rect,
1284 surface_screen, &dst_rect);
1290 src_rect.x = src_x + i * melt_pixels;
1292 src_rect.w = melt_pixels;
1293 src_rect.h = height;
1295 dst_rect.x = dst_x + i * melt_pixels;
1298 if (steps_done >= steps_final)
1299 SDL_BlitSurface(surface_target, &src_rect,
1300 surface_screen, &dst_rect);
1304 if (steps_done >= steps_final)
1306 if (draw_border_function != NULL)
1307 draw_border_function();
1309 UpdateScreen_WithFrameDelay(&dst_rect2);
1311 if (PendingEscapeKeyEvent())
1316 else if (fade_mode == FADE_MODE_CURTAIN)
1320 int xx_size = width / 2;
1322 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1324 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1326 for (xx = 0; xx < xx_size;)
1328 time_last = time_current;
1329 time_current = SDL_GetTicks();
1330 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1331 xx_final = MIN(MAX(0, xx), xx_size);
1336 src_rect.h = height;
1341 // draw new (target) image to screen buffer
1342 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1344 if (xx_final < xx_size)
1346 src_rect.w = xx_size - xx_final;
1347 src_rect.h = height;
1349 // draw old (source) image to screen buffer (left side)
1351 src_rect.x = src_x + xx_final;
1354 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1356 // draw old (source) image to screen buffer (right side)
1358 src_rect.x = src_x + xx_size;
1359 dst_rect.x = dst_x + xx_size + xx_final;
1361 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1364 if (draw_border_function != NULL)
1365 draw_border_function();
1367 // only update the region of the screen that is affected from fading
1368 UpdateScreen_WithFrameDelay(&dst_rect2);
1370 if (PendingEscapeKeyEvent())
1374 else // fading in, fading out or cross-fading
1379 for (alpha = 0.0; alpha < 255.0;)
1381 time_last = time_current;
1382 time_current = SDL_GetTicks();
1383 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1384 alpha_final = MIN(MAX(0, alpha), 255);
1386 // draw existing (source) image to screen buffer
1387 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1389 // draw new (target) image to screen buffer using alpha blending
1390 SDLSetAlpha(surface_target, TRUE, alpha_final);
1391 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1393 if (draw_border_function != NULL)
1394 draw_border_function();
1396 // only update the region of the screen that is affected from fading
1397 UpdateScreen_WithFrameDelay(&dst_rect);
1399 if (PendingEscapeKeyEvent())
1405 Delay_WithScreenUpdates(post_delay);
1407 // restore function for drawing global masked border
1408 gfx.draw_global_border_function = draw_global_border_function;
1410 // after fading in, restore backbuffer (without animation graphics)
1411 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1412 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1415 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1416 int to_x, int to_y, Uint32 color)
1418 SDL_Surface *surface = dst_bitmap->surface;
1422 swap_numbers(&from_x, &to_x);
1425 swap_numbers(&from_y, &to_y);
1429 rect.w = (to_x - from_x + 1);
1430 rect.h = (to_y - from_y + 1);
1432 SDL_FillRect(surface, &rect, color);
1435 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1436 int to_x, int to_y, Uint32 color)
1438 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1441 #if ENABLE_UNUSED_CODE
1442 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1443 int num_points, Uint32 color)
1448 for (i = 0; i < num_points - 1; i++)
1450 for (x = 0; x < line_width; x++)
1452 for (y = 0; y < line_width; y++)
1454 int dx = x - line_width / 2;
1455 int dy = y - line_width / 2;
1457 if ((x == 0 && y == 0) ||
1458 (x == 0 && y == line_width - 1) ||
1459 (x == line_width - 1 && y == 0) ||
1460 (x == line_width - 1 && y == line_width - 1))
1463 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1464 points[i + 1].x + dx, points[i + 1].y + dy, color);
1471 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1473 SDL_Surface *surface = src_bitmap->surface;
1475 switch (surface->format->BytesPerPixel)
1477 case 1: // assuming 8-bpp
1479 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1483 case 2: // probably 15-bpp or 16-bpp
1485 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1489 case 3: // slow 24-bpp mode; usually not used
1492 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1496 shift = surface->format->Rshift;
1497 color |= *(pix + shift / 8) >> shift;
1498 shift = surface->format->Gshift;
1499 color |= *(pix + shift / 8) >> shift;
1500 shift = surface->format->Bshift;
1501 color |= *(pix + shift / 8) >> shift;
1507 case 4: // probably 32-bpp
1509 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1518 // ============================================================================
1519 // The following functions were taken from the SGE library
1520 // (SDL Graphics Extension Library) by Anders Lindström
1521 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1522 // ============================================================================
1524 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1526 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1528 switch (surface->format->BytesPerPixel)
1533 *((Uint8 *)surface->pixels + y * surface->pitch + x) = color;
1539 // Probably 15-bpp or 16-bpp
1540 *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x) = color;
1546 // Slow 24-bpp mode, usually not used
1550 // Gack - slow, but endian correct
1551 pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1552 shift = surface->format->Rshift;
1553 *(pix + shift / 8) = color>>shift;
1554 shift = surface->format->Gshift;
1555 *(pix + shift / 8) = color>>shift;
1556 shift = surface->format->Bshift;
1557 *(pix + shift / 8) = color>>shift;
1564 *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x) = color;
1572 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1573 Uint8 R, Uint8 G, Uint8 B)
1575 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1578 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1580 *((Uint8 *)surface->pixels + y * surface->pitch + x) = color;
1583 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1585 *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x) = color;
1588 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1593 // Gack - slow, but endian correct
1594 pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1595 shift = surface->format->Rshift;
1596 *(pix + shift / 8) = color>>shift;
1597 shift = surface->format->Gshift;
1598 *(pix + shift / 8) = color>>shift;
1599 shift = surface->format->Bshift;
1600 *(pix + shift / 8) = color>>shift;
1603 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1605 *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x) = color;
1608 static void _PutPixelX(SDL_Surface *dest, Sint16 x, Sint16 y, Uint32 color)
1610 switch (dest->format->BytesPerPixel)
1613 *((Uint8 *)dest->pixels + y * dest->pitch + x) = color;
1617 *((Uint16 *)dest->pixels + y * dest->pitch / 2 + x) = color;
1621 _PutPixel24(dest, x, y, color);
1625 *((Uint32 *)dest->pixels + y * dest->pitch / 4 + x) = color;
1631 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1633 if (SDL_MUSTLOCK(surface))
1635 if (SDL_LockSurface(surface) < 0)
1641 _PutPixel(surface, x, y, color);
1643 if (SDL_MUSTLOCK(surface))
1645 SDL_UnlockSurface(surface);
1650 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1651 Uint8 r, Uint8 g, Uint8 b)
1653 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1656 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1658 if (y >= 0 && y <= dest->h - 1)
1660 switch (dest->format->BytesPerPixel)
1663 return y * dest->pitch;
1667 return y * dest->pitch / 2;
1671 return y * dest->pitch;
1675 return y * dest->pitch / 4;
1683 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1686 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1688 switch (surface->format->BytesPerPixel)
1693 *((Uint8 *)surface->pixels + ypitch + x) = color;
1699 // Probably 15-bpp or 16-bpp
1700 *((Uint16 *)surface->pixels + ypitch + x) = color;
1706 // Slow 24-bpp mode, usually not used
1710 // Gack - slow, but endian correct
1711 pix = (Uint8 *)surface->pixels + ypitch + x * 3;
1712 shift = surface->format->Rshift;
1713 *(pix + shift / 8) = color>>shift;
1714 shift = surface->format->Gshift;
1715 *(pix + shift / 8) = color>>shift;
1716 shift = surface->format->Bshift;
1717 *(pix + shift / 8) = color>>shift;
1724 *((Uint32 *)surface->pixels + ypitch + x) = color;
1731 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1736 if (SDL_MUSTLOCK(Surface))
1738 if (SDL_LockSurface(Surface) < 0)
1752 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1756 if (x2 > Surface->w - 1)
1757 x2 = Surface->w - 1;
1764 SDL_FillRect(Surface, &l, Color);
1766 if (SDL_MUSTLOCK(Surface))
1768 SDL_UnlockSurface(Surface);
1772 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1773 Uint8 R, Uint8 G, Uint8 B)
1775 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1778 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1791 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1795 if (x2 > Surface->w - 1)
1796 x2 = Surface->w - 1;
1803 SDL_FillRect(Surface, &l, Color);
1806 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1811 if (SDL_MUSTLOCK(Surface))
1813 if (SDL_LockSurface(Surface) < 0)
1827 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1831 if (y2 > Surface->h - 1)
1832 y2 = Surface->h - 1;
1839 SDL_FillRect(Surface, &l, Color);
1841 if (SDL_MUSTLOCK(Surface))
1843 SDL_UnlockSurface(Surface);
1847 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1848 Uint8 R, Uint8 G, Uint8 B)
1850 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1853 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1866 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1870 if (y2 > Surface->h - 1)
1871 y2 = Surface->h - 1;
1878 SDL_FillRect(Surface, &l, Color);
1882 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1883 Sint16 x2, Sint16 y2, Uint32 Color,
1884 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1887 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1892 sdx = (dx < 0) ? -1 : 1;
1893 sdy = (dy < 0) ? -1 : 1;
1905 for (x = 0; x < dx; x++)
1907 Callback(Surface, px, py, Color);
1921 for (y = 0; y < dy; y++)
1923 Callback(Surface, px, py, Color);
1938 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1939 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1940 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1943 sge_DoLine(Surface, X1, Y1, X2, Y2,
1944 SDL_MapRGB(Surface->format, R, G, B), Callback);
1948 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1951 if (SDL_MUSTLOCK(Surface))
1953 if (SDL_LockSurface(Surface) < 0)
1958 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1960 // unlock the display
1961 if (SDL_MUSTLOCK(Surface))
1963 SDL_UnlockSurface(Surface);
1968 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1969 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1971 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1975 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1977 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1981 // ----------------------------------------------------------------------------
1982 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1983 // ----------------------------------------------------------------------------
1985 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1986 int src_x, int src_y, int width, int height,
1987 int dst_x, int dst_y)
1991 for (y = 0; y < height; y++)
1993 for (x = 0; x < width; x++)
1995 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1997 if (pixel != BLACK_PIXEL)
1998 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
2004 // ============================================================================
2005 // The following functions were taken from the SDL_gfx library version 2.0.3
2006 // (Rotozoomer) by Andreas Schiffler
2007 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
2008 // ============================================================================
2010 // ----------------------------------------------------------------------------
2013 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
2014 // ----------------------------------------------------------------------------
2024 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
2027 tColorRGBA *sp, *csp, *dp;
2031 sp = csp = (tColorRGBA *) src->pixels;
2032 dp = (tColorRGBA *) dst->pixels;
2033 dgap = dst->pitch - dst->w * 4;
2035 for (y = 0; y < dst->h; y++)
2039 for (x = 0; x < dst->w; x++)
2041 tColorRGBA *sp0 = sp;
2042 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
2043 tColorRGBA *sp00 = &sp0[0];
2044 tColorRGBA *sp01 = &sp0[1];
2045 tColorRGBA *sp10 = &sp1[0];
2046 tColorRGBA *sp11 = &sp1[1];
2049 // create new color pixel from all four source color pixels
2050 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
2051 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
2052 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
2053 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
2058 // advance source pointers
2061 // advance destination pointer
2065 // advance source pointer
2066 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2068 // advance destination pointers
2069 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2075 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2077 int x, y, *sax, *say, *csax, *csay;
2079 tColorRGBA *sp, *csp, *csp0, *dp;
2082 // use specialized zoom function when scaling down to exactly half size
2083 if (src->w == 2 * dst->w &&
2084 src->h == 2 * dst->h)
2085 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2088 sx = (float) src->w / (float) dst->w;
2089 sy = (float) src->h / (float) dst->h;
2091 // allocate memory for row increments
2092 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2093 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2095 // precalculate row increments
2096 for (x = 0; x <= dst->w; x++)
2097 *csax++ = (int)(sx * x);
2099 for (y = 0; y <= dst->h; y++)
2100 *csay++ = (int)(sy * y);
2103 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2104 dp = (tColorRGBA *) dst->pixels;
2105 dgap = dst->pitch - dst->w * 4;
2108 for (y = 0; y < dst->h; y++)
2113 for (x = 0; x < dst->w; x++)
2118 // advance source pointers
2122 // advance destination pointer
2126 // advance source pointer
2128 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2130 // advance destination pointers
2131 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2140 // ----------------------------------------------------------------------------
2143 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2144 // ----------------------------------------------------------------------------
2146 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2148 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2149 Uint8 *sp, *dp, *csp;
2153 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2154 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2156 // allocate memory for row increments
2157 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2158 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2160 // precalculate row increments
2163 for (x = 0; x < dst->w; x++)
2166 *csax = (csx >> 16);
2173 for (y = 0; y < dst->h; y++)
2176 *csay = (csy >> 16);
2183 for (x = 0; x < dst->w; x++)
2191 for (y = 0; y < dst->h; y++)
2198 sp = csp = (Uint8 *) src->pixels;
2199 dp = (Uint8 *) dst->pixels;
2200 dgap = dst->pitch - dst->w;
2204 for (y = 0; y < dst->h; y++)
2208 for (x = 0; x < dst->w; x++)
2213 // advance source pointers
2217 // advance destination pointer
2221 // advance source pointer (for row)
2222 csp += ((*csay) * src->pitch);
2225 // advance destination pointers
2235 // ----------------------------------------------------------------------------
2238 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2239 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2240 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2241 // into a 32bit RGBA format on the fly.
2242 // ----------------------------------------------------------------------------
2244 SDL_Surface *SDLZoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2246 SDL_Surface *zoom_src = NULL;
2247 SDL_Surface *zoom_dst = NULL;
2248 boolean is_converted = FALSE;
2255 // determine if source surface is 32 bit or 8 bit
2256 is_32bit = (src->format->BitsPerPixel == 32);
2258 if (is_32bit || src->format->BitsPerPixel == 8)
2260 // use source surface 'as is'
2265 // new source surface is 32 bit with a defined RGB ordering
2266 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2267 0x000000ff, 0x0000ff00, 0x00ff0000,
2268 (src->format->Amask ? 0xff000000 : 0));
2269 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2271 is_converted = TRUE;
2274 // allocate surface to completely contain the zoomed surface
2277 // target surface is 32 bit with source RGBA/ABGR ordering
2278 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2279 zoom_src->format->Rmask,
2280 zoom_src->format->Gmask,
2281 zoom_src->format->Bmask,
2282 zoom_src->format->Amask);
2286 // target surface is 8 bit
2287 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2291 // lock source surface
2292 SDL_LockSurface(zoom_src);
2294 // check which kind of surface we have
2297 // call the 32 bit transformation routine to do the zooming
2298 zoomSurfaceRGBA(zoom_src, zoom_dst);
2303 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2304 zoom_dst->format->palette->colors[i] =
2305 zoom_src->format->palette->colors[i];
2306 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2308 // call the 8 bit transformation routine to do the zooming
2309 zoomSurfaceY(zoom_src, zoom_dst);
2312 // unlock source surface
2313 SDL_UnlockSurface(zoom_src);
2315 // free temporary surface
2317 SDL_FreeSurface(zoom_src);
2319 // return destination surface
2323 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2325 SDL_Surface *new_surface;
2327 if (surface == NULL)
2330 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2331 Fail("SDLGetNativeSurface() failed");
2333 // remove alpha channel from native non-transparent surface, if defined
2334 SDLSetAlpha(new_surface, FALSE, 0);
2336 // remove transparent color from native non-transparent surface, if defined
2337 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
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();