1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
19 #define ENABLE_UNUSED_CODE 0 // currently unused functions
21 #define DEBUG_JOYSTICKS 0
24 // ============================================================================
26 // ============================================================================
28 // SDL internal variables
29 static SDL_Window *sdl_window = NULL;
30 static SDL_Renderer *sdl_renderer = NULL;
31 static SDL_Texture *sdl_texture_stream = NULL;
32 static SDL_Texture *sdl_texture_target = NULL;
33 static boolean fullscreen_enabled = FALSE;
34 static boolean limit_screen_updates = FALSE;
37 // functions from SGE library
38 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
40 #if defined(USE_TOUCH_INPUT_OVERLAY)
41 // functions to draw overlay graphics for touch device input
42 static void DrawTouchInputOverlay(void);
43 static void DrawTouchGadgetsOverlay(void);
46 void SDLLimitScreenUpdates(boolean enable)
48 limit_screen_updates = enable;
51 static void FinalizeScreen(int draw_target)
53 // copy global animations to render target buffer, if defined (below border)
54 if (gfx.draw_global_anim_function != NULL)
55 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
57 // copy global masked border to render target buffer, if defined
58 if (gfx.draw_global_border_function != NULL)
59 gfx.draw_global_border_function(draw_target);
61 // copy global animations to render target buffer, if defined (above border)
62 if (gfx.draw_global_anim_function != NULL)
63 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
65 // copy tile selection cursor to render target buffer, if defined (part 1)
66 if (gfx.draw_tile_cursor_function != NULL)
67 gfx.draw_tile_cursor_function(draw_target, TRUE);
69 // copy envelope request to render target buffer, if needed (above all)
70 if (gfx.draw_envelope_request_function != NULL)
71 gfx.draw_envelope_request_function(draw_target);
73 // copy tile selection cursor to render target buffer, if defined (part 2)
74 if (gfx.draw_tile_cursor_function != NULL)
75 gfx.draw_tile_cursor_function(draw_target, FALSE);
77 // copy global animations to render target buffer, if defined (mouse pointer)
78 if (gfx.draw_global_anim_function != NULL)
79 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_3);
82 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
87 static DelayCounter update_screen_delay = { 50 }; // (milliseconds)
88 SDL_Surface *screen = backbuffer->surface;
90 if (limit_screen_updates &&
91 !DelayReached(&update_screen_delay))
94 LimitScreenUpdates(FALSE);
98 static int LastFrameCounter = 0;
99 boolean changed = (FrameCounter != LastFrameCounter);
101 Debug("internal:frame", "FrameCounter == %d [%s]", FrameCounter,
102 (changed ? "-" : "SAME FRAME UPDATED"));
104 LastFrameCounter = FrameCounter;
107 if (FrameCounter % 2)
113 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
114 gfx.final_screen_bitmap != NULL) // may not be initialized yet
116 // draw global animations using bitmaps instead of using textures
117 // to prevent texture scaling artefacts (this is potentially slower)
119 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
120 gfx.win_xsize, gfx.win_ysize, 0, 0);
122 FinalizeScreen(DRAW_TO_SCREEN);
124 screen = gfx.final_screen_bitmap->surface;
126 // force full window redraw
130 SDL_Texture *sdl_texture = sdl_texture_stream;
132 // deactivate use of target texture if render targets are not supported
133 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
134 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
135 sdl_texture_target == NULL)
136 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
138 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
139 sdl_texture = sdl_texture_target;
143 int bytes_x = screen->pitch / video.width;
144 int bytes_y = screen->pitch;
146 SDL_UpdateTexture(sdl_texture, rect,
147 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
152 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
155 int xoff = video.screen_xoffset;
156 int yoff = video.screen_yoffset;
157 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
158 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
159 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
161 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
162 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
163 dst_rect2 = &dst_rect_screen;
165 dst_rect1 = &dst_rect_screen;
167 #if defined(HAS_SCREEN_KEYBOARD)
168 SDL_Rect src_rect_up = { 0, 0, video.width, video.height };
169 SDL_Rect dst_rect_up = dst_rect_screen;
171 if (video.shifted_up || video.shifted_up_delay.count)
173 int time_current = SDL_GetTicks();
174 int pos = video.shifted_up_pos;
175 int pos_last = video.shifted_up_pos_last;
177 if (!DelayReachedExt(&video.shifted_up_delay, time_current))
179 int delay_count = time_current - video.shifted_up_delay.count;
180 int delay_value = video.shifted_up_delay.value;
182 pos = pos_last + (pos - pos_last) * delay_count / delay_value;
186 video.shifted_up_pos_last = pos;
187 video.shifted_up_delay.count = 0;
191 src_rect_up.h = video.height - pos;
192 dst_rect_up.h = video.height - pos;
194 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
195 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
197 src_rect2 = &src_rect_up;
198 dst_rect2 = &dst_rect_up;
202 src_rect1 = &src_rect_up;
203 dst_rect1 = &dst_rect_up;
208 // clear render target buffer
209 SDL_RenderClear(sdl_renderer);
211 // set renderer to use target texture for rendering
212 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
213 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
214 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
216 // copy backbuffer texture to render target buffer
217 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
218 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
220 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
221 FinalizeScreen(DRAW_TO_SCREEN);
223 // when using target texture, copy it to screen buffer
224 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
225 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
227 SDL_SetRenderTarget(sdl_renderer, NULL);
228 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
231 #if defined(USE_TOUCH_INPUT_OVERLAY)
232 // draw overlay graphics for touch device input, if needed
233 DrawTouchInputOverlay();
235 // draw overlay gadgets for touch device input, if needed
236 DrawTouchGadgetsOverlay();
239 // global synchronization point of the game to align video frame delay
240 if (with_frame_delay)
241 WaitUntilDelayReached(&video.frame_delay);
243 video.frame_counter++;
245 // show render target buffer on screen
246 SDL_RenderPresent(sdl_renderer);
249 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
251 PumpEvents(); // execute event filter actions while waiting
253 UpdateScreenExt(rect, TRUE);
256 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
258 UpdateScreenExt(rect, FALSE);
261 void Delay_WithScreenUpdates(unsigned int delay)
263 unsigned int time_current = SDL_GetTicks();
264 unsigned int time_delayed = time_current + delay;
266 while (time_current < time_delayed)
268 // updating the screen contains waiting for frame delay (non-busy)
269 UpdateScreen_WithFrameDelay(NULL);
271 time_current = SDL_GetTicks();
275 static void SDLSetWindowIcon(char *basename)
277 // (setting the window icon on Mac OS X would replace the high-quality
278 // dock icon with the currently smaller (and uglier) icon from file)
280 #if !defined(PLATFORM_MAC)
281 char *filename = getCustomImageFilename(basename);
282 SDL_Surface *surface;
284 if (filename == NULL)
286 Warn("SDLSetWindowIcon(): cannot find file '%s'", basename);
291 if ((surface = IMG_Load(filename)) == NULL)
293 Warn("IMG_Load('%s') failed: %s", basename, SDL_GetError());
298 // set transparent color
299 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
300 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
302 SDL_SetWindowIcon(sdl_window, surface);
306 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
307 SDL_PixelFormat *format2)
309 return (format1->format == format2->format &&
310 format1->BitsPerPixel == format2->BitsPerPixel &&
311 format1->BytesPerPixel == format2->BytesPerPixel &&
312 format1->Rmask == format2->Rmask &&
313 format1->Gmask == format2->Gmask &&
314 format1->Bmask == format2->Bmask);
317 static void SDLCopyColorKey(SDL_Surface *src_surface, SDL_Surface *dst_surface)
322 // check if source surface has a color key
323 if (SDL_GetColorKey(src_surface, &color_key) == 0)
325 // get RGB values of color key of source surface
326 SDL_GetRGB(color_key, src_surface->format, &r, &g, &b);
328 // get color key from RGB values in destination surface format
329 color_key = SDL_MapRGB(dst_surface->format, r, g, b);
331 // set color key in destination surface
332 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL, color_key);
336 // unset color key in destination surface
337 SDL_SetColorKey(dst_surface, UNSET_TRANSPARENT_PIXEL, 0);
341 static boolean SDLHasColorKey(SDL_Surface *surface)
345 return (SDL_GetColorKey(surface, &color_key) == 0);
348 static boolean SDLHasAlpha(SDL_Surface *surface)
350 SDL_BlendMode blend_mode;
352 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
355 return (blend_mode == SDL_BLENDMODE_BLEND);
358 static void SDLSetSurfaceAlpha(SDL_Surface *surface, boolean set, int alpha)
360 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
362 SDL_SetSurfaceBlendMode(surface, blend_mode);
363 SDL_SetSurfaceAlphaMod(surface, alpha);
366 static void SDLSetTextureAlpha(SDL_Texture *texture, boolean set, int alpha)
368 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
370 SDL_SetTextureBlendMode(texture, blend_mode);
371 SDL_SetTextureAlphaMod(texture, alpha);
374 static void SDLSetBitmapAlpha(Bitmap *bitmap, boolean is_texture,
377 int alpha_next_blit = bitmap->alpha_next_blit;
379 // alpha value must be requested every time before blitting, if needed
380 bitmap->alpha_next_blit = -1;
382 // nothing to do if requested alpha value is already set
383 if (bitmap->alpha[is_texture][is_masked] == alpha_next_blit)
386 // store requested alpha value for masked/unmasked surface/texture
387 bitmap->alpha[is_texture][is_masked] = alpha_next_blit;
389 // set blend mode if bitmap is masked or if alpha value is defined
390 boolean set_blend_mode = (is_masked || alpha_next_blit != -1);
392 // if alpha value is undefined, use default (opaque) alpha value
393 if (alpha_next_blit == -1)
394 alpha_next_blit = SDL_ALPHA_OPAQUE;
397 SDLSetTextureAlpha(is_masked ? bitmap->texture_masked : bitmap->texture,
398 set_blend_mode, alpha_next_blit);
400 SDLSetSurfaceAlpha(is_masked ? bitmap->surface_masked : bitmap->surface,
401 set_blend_mode, alpha_next_blit);
404 void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
406 SDLSetSurfaceAlpha(surface, set, alpha);
409 const char *SDLGetRendererName(void)
411 static SDL_RendererInfo renderer_info;
413 SDL_GetRendererInfo(sdl_renderer, &renderer_info);
415 return renderer_info.name;
418 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
420 SDL_Surface *new_surface;
425 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
426 Fail("SDLGetNativeSurface() failed");
428 // remove alpha channel from native non-transparent surface, if defined
429 SDLSetAlpha(new_surface, FALSE, 0);
431 // remove transparent color from native non-transparent surface, if defined
432 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
437 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
439 SDL_PixelFormat format;
440 SDL_Surface *new_surface;
445 if (backbuffer && backbuffer->surface)
447 format = *backbuffer->surface->format;
448 format.Amask = surface->format->Amask; // keep alpha channel
452 format = *surface->format;
455 new_surface = SDL_ConvertSurface(surface, &format, 0);
457 if (new_surface == NULL)
458 Fail("SDL_ConvertSurface() failed: %s", SDL_GetError());
460 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
461 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
462 SDLCopyColorKey(surface, new_surface);
467 boolean SDLSetNativeSurface(SDL_Surface **surface)
469 SDL_Surface *new_surface;
471 if (surface == NULL ||
473 backbuffer == NULL ||
474 backbuffer->surface == NULL)
477 // if pixel format already optimized for destination surface, do nothing
478 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
481 new_surface = SDLGetNativeSurface(*surface);
483 SDL_FreeSurface(*surface);
485 *surface = new_surface;
490 SDL_Surface *SDLCreateNativeSurface(int width, int height, int depth)
492 if (program.headless)
495 SDL_Surface *surface = SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
498 Fail("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
500 SDLSetNativeSurface(&surface);
505 Bitmap *SDLGetBitmapFromSurface(SDL_Surface *surface)
507 int width = surface->w;
508 int height = surface->h;
509 int depth = video.default_depth;
510 Bitmap *bitmap = CreateBitmap(width, height, depth);
512 // free default surface (not needed anymore)
513 SDL_FreeSurface(bitmap->surface);
515 // get native, non-transparent surface from original surface
516 bitmap->surface = SDLGetOpaqueSurface(surface);
518 // get native, potentially transparent surface from original surface
519 bitmap->surface_masked = SDLGetNativeSurface(surface);
521 // set black pixel to transparent if no alpha channel / transparent color
522 if (!SDLHasAlpha(bitmap->surface_masked) &&
523 !SDLHasColorKey(bitmap->surface_masked))
524 SDL_SetColorKey(bitmap->surface_masked, SET_TRANSPARENT_PIXEL,
525 SDL_MapRGB(bitmap->surface_masked->format, 0x00, 0x00, 0x00));
530 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
532 if (program.headless)
535 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
538 Fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
543 void SDLCreateBitmapTextures(Bitmap *bitmap)
549 SDL_DestroyTexture(bitmap->texture);
550 if (bitmap->texture_masked)
551 SDL_DestroyTexture(bitmap->texture_masked);
553 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
554 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
557 void SDLFreeBitmapTextures(Bitmap *bitmap)
563 SDL_DestroyTexture(bitmap->texture);
564 if (bitmap->texture_masked)
565 SDL_DestroyTexture(bitmap->texture_masked);
567 bitmap->texture = NULL;
568 bitmap->texture_masked = NULL;
571 void SDLInitVideoDisplay(void)
573 // set hint to select render driver as specified in setup config file
574 if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
575 SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
577 // initialize SDL video
578 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
579 Fail("SDL_InitSubSystem() failed: %s", SDL_GetError());
581 // set default SDL depth
582 video.default_depth = 32; // (how to determine video depth in SDL2?)
584 // Code used with SDL 1.2:
585 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
588 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
590 if (program.headless)
593 video.window_scaling_percent = setup.window_scaling_percent;
594 video.window_scaling_quality = setup.window_scaling_quality;
596 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
598 // SDL 2.0: support for (desktop) fullscreen mode available
599 video.fullscreen_available = TRUE;
601 // open SDL video output device (window or fullscreen mode)
602 if (!SDLSetVideoMode(fullscreen))
603 Fail("setting video mode failed");
605 // !!! SDL2 can only set the window icon if the window already exists !!!
607 SDLSetWindowIcon(program.icon_filename);
609 // set window and icon title
613 static void SDLInitVideoBuffer_DrawBuffer(void)
615 /* SDL cannot directly draw to the visible video framebuffer like X11,
616 but always uses a backbuffer, which is then blitted to the visible
617 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
618 visible video framebuffer with 'SDL_Flip', if the hardware supports
619 this). Therefore do not use an additional backbuffer for drawing, but
620 use a symbolic buffer (distinguishable from the SDL backbuffer) called
621 'window', which indicates that the SDL backbuffer should be updated to
622 the visible video framebuffer when attempting to blit to it.
624 For convenience, it seems to be a good idea to create this symbolic
625 buffer 'window' at the same size as the SDL backbuffer. Although it
626 should never be drawn to directly, it would do no harm nevertheless. */
628 // create additional (symbolic) buffer for double-buffering
629 ReCreateBitmap(&window, video.width, video.height);
631 // create dummy drawing buffer for headless mode, if needed
632 if (program.headless)
633 ReCreateBitmap(&backbuffer, video.width, video.height);
636 void SDLInitVideoBuffer(boolean fullscreen)
638 SDLInitVideoBuffer_VideoBuffer(fullscreen);
639 SDLInitVideoBuffer_DrawBuffer();
642 static boolean SDLCreateScreen(boolean fullscreen)
644 SDL_Surface *new_surface = NULL;
646 int surface_flags_window = SURFACE_FLAGS;
647 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
650 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
652 video.vsync_mode = VSYNC_MODE_OFF;
654 if (!strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF))
656 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
657 video.vsync_mode = VSYNC_MODE_NORMAL;
660 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
661 _without_ enabling 2D/3D acceleration and/or guest additions installed,
662 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
663 it will try to use accelerated graphics and apparently fails miserably) */
664 int renderer_flags = SDL_RENDERER_SOFTWARE;
667 int width = video.width;
668 int height = video.height;
669 int screen_width = video.screen_width;
670 int screen_height = video.screen_height;
671 int surface_flags = (fullscreen ? surface_flags_fullscreen :
672 surface_flags_window);
673 int display_nr = options.display_nr;
675 // default window size is unscaled
676 video.window_width = screen_width;
677 video.window_height = screen_height;
679 // store if initial screen mode is fullscreen mode when changing screen size
680 video.fullscreen_initial = fullscreen;
682 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
684 video.window_width = window_scaling_factor * screen_width;
685 video.window_height = window_scaling_factor * screen_height;
687 if (sdl_texture_stream)
689 SDL_DestroyTexture(sdl_texture_stream);
690 sdl_texture_stream = NULL;
693 if (sdl_texture_target)
695 SDL_DestroyTexture(sdl_texture_target);
696 sdl_texture_target = NULL;
699 if (!(fullscreen && fullscreen_enabled))
703 SDL_DestroyRenderer(sdl_renderer);
709 SDL_SetWindowSize(sdl_window, video.window_width, video.window_height);
713 if (sdl_window == NULL)
714 sdl_window = SDL_CreateWindow(program.window_title,
715 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
716 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
721 if (sdl_window != NULL)
723 if (sdl_renderer == NULL)
724 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
726 if (sdl_renderer != NULL)
728 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
729 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
731 // required for setting adaptive vsync when using OpenGL renderer
732 SDLSetScreenVsyncMode(setup.vsync_mode);
734 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
735 SDL_PIXELFORMAT_ARGB8888,
736 SDL_TEXTUREACCESS_STREAMING,
739 if (SDL_RenderTargetSupported(sdl_renderer))
740 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
741 SDL_PIXELFORMAT_ARGB8888,
742 SDL_TEXTUREACCESS_TARGET,
745 if (sdl_texture_stream != NULL)
747 // use SDL default values for RGB masks and no alpha channel
748 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
750 if (new_surface == NULL)
751 Warn("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
755 Warn("SDL_CreateTexture() failed: %s", SDL_GetError());
760 Warn("SDL_CreateRenderer() failed: %s", SDL_GetError());
765 Warn("SDL_CreateWindow() failed: %s", SDL_GetError());
768 SDLSetScreenProperties();
770 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
771 if (new_surface != NULL)
772 fullscreen_enabled = fullscreen;
774 if (backbuffer == NULL)
775 backbuffer = CreateBitmapStruct();
777 backbuffer->width = video.width;
778 backbuffer->height = video.height;
780 if (backbuffer->surface)
781 SDL_FreeSurface(backbuffer->surface);
783 backbuffer->surface = new_surface;
785 return (new_surface != NULL);
788 boolean SDLSetVideoMode(boolean fullscreen)
790 boolean success = FALSE;
794 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
796 // switch display to fullscreen mode, if available
797 success = SDLCreateScreen(TRUE);
801 // switching display to fullscreen mode failed -- do not try it again
802 video.fullscreen_available = FALSE;
806 video.fullscreen_enabled = TRUE;
810 if ((!fullscreen && video.fullscreen_enabled) || !success)
812 // switch display to window mode
813 success = SDLCreateScreen(FALSE);
817 // switching display to window mode failed -- should not happen
821 video.fullscreen_enabled = FALSE;
822 video.window_scaling_percent = setup.window_scaling_percent;
823 video.window_scaling_quality = setup.window_scaling_quality;
825 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
829 SDLRedrawWindow(); // map window
834 void SDLSetWindowTitle(void)
836 if (sdl_window == NULL)
839 SDL_SetWindowTitle(sdl_window, program.window_title);
842 void SDLSetWindowScaling(int window_scaling_percent)
844 if (sdl_window == NULL)
847 float window_scaling_factor = (float)window_scaling_percent / 100;
848 int new_window_width = (int)(window_scaling_factor * video.screen_width);
849 int new_window_height = (int)(window_scaling_factor * video.screen_height);
851 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
853 video.window_scaling_percent = window_scaling_percent;
854 video.window_width = new_window_width;
855 video.window_height = new_window_height;
860 void SDLSetWindowScalingQuality(char *window_scaling_quality)
862 SDL_Texture *new_texture;
864 if (sdl_texture_stream == NULL)
867 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
869 new_texture = SDL_CreateTexture(sdl_renderer,
870 SDL_PIXELFORMAT_ARGB8888,
871 SDL_TEXTUREACCESS_STREAMING,
872 video.width, video.height);
874 if (new_texture != NULL)
876 SDL_DestroyTexture(sdl_texture_stream);
878 sdl_texture_stream = new_texture;
881 if (SDL_RenderTargetSupported(sdl_renderer))
882 new_texture = SDL_CreateTexture(sdl_renderer,
883 SDL_PIXELFORMAT_ARGB8888,
884 SDL_TEXTUREACCESS_TARGET,
885 video.width, video.height);
889 if (new_texture != NULL)
891 SDL_DestroyTexture(sdl_texture_target);
893 sdl_texture_target = new_texture;
898 video.window_scaling_quality = window_scaling_quality;
901 void SDLSetWindowFullscreen(boolean fullscreen)
903 if (sdl_window == NULL)
906 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
908 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
909 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
911 // if screen size was changed in fullscreen mode, correct desktop window size
912 if (!fullscreen && video.fullscreen_initial)
914 SDLSetWindowScaling(setup.window_scaling_percent);
915 SDL_SetWindowPosition(sdl_window,
916 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr),
917 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr));
919 video.fullscreen_initial = FALSE;
923 void SDLSetDisplaySize(void)
925 if (sdl_renderer != NULL)
929 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
931 video.display_width = w;
932 video.display_height = h;
935 Debug("video", "SDL renderer size: %d x %d",
936 video.display_width, video.display_height);
941 SDL_Rect display_bounds;
943 SDL_GetDisplayBounds(0, &display_bounds);
945 video.display_width = display_bounds.w;
946 video.display_height = display_bounds.h;
949 Debug("video", "SDL display size: %d x %d",
950 video.display_width, video.display_height);
955 void SDLSetScreenSizeAndOffsets(int width, int height)
957 // set default video screen size and offsets
958 video.screen_width = width;
959 video.screen_height = height;
960 video.screen_xoffset = 0;
961 video.screen_yoffset = 0;
963 #if defined(USE_COMPLETE_DISPLAY)
964 float ratio_video = (float) width / height;
965 float ratio_display = (float) video.display_width / video.display_height;
967 if (ratio_video != ratio_display)
969 // adjust drawable screen size to cover the whole device display
971 if (ratio_video < ratio_display)
972 video.screen_width *= ratio_display / ratio_video;
974 video.screen_height *= ratio_video / ratio_display;
976 video.screen_xoffset = (video.screen_width - width) / 2;
977 video.screen_yoffset = (video.screen_height - height) / 2;
980 Debug("video", "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
982 video.screen_width, video.screen_height,
983 ratio_video, ratio_display);
989 void SDLSetScreenSizeForRenderer(int width, int height)
991 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
994 void SDLSetScreenProperties(void)
997 SDLSetScreenSizeAndOffsets(video.width, video.height);
998 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
1000 SetOverlayGridSizeAndButtons();
1003 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
1005 video.screen_rendering_mode =
1006 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
1007 SPECIAL_RENDERING_BITMAP :
1008 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
1009 SPECIAL_RENDERING_TARGET:
1010 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
1011 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
1014 void SDLSetScreenVsyncMode(char *vsync_mode)
1016 // changing vsync mode without re-creating renderer only supported by OpenGL
1017 if (!strPrefixLower((char *)SDLGetRendererName(), "opengl"))
1020 int interval = VSYNC_MODE_STR_TO_INT(vsync_mode);
1021 int result = SDL_GL_SetSwapInterval(interval);
1023 // if adaptive vsync requested, but not supported, retry with normal vsync
1024 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
1026 interval = VSYNC_MODE_NORMAL;
1028 result = SDL_GL_SetSwapInterval(interval);
1032 interval = VSYNC_MODE_OFF;
1034 video.vsync_mode = interval;
1037 void SDLRedrawWindow(void)
1039 UpdateScreen_WithoutFrameDelay(NULL);
1042 void SDLFreeBitmapPointers(Bitmap *bitmap)
1044 if (bitmap->surface)
1045 SDL_FreeSurface(bitmap->surface);
1046 if (bitmap->surface_masked)
1047 SDL_FreeSurface(bitmap->surface_masked);
1049 bitmap->surface = NULL;
1050 bitmap->surface_masked = NULL;
1052 if (bitmap->texture)
1053 SDL_DestroyTexture(bitmap->texture);
1054 if (bitmap->texture_masked)
1055 SDL_DestroyTexture(bitmap->texture_masked);
1057 bitmap->texture = NULL;
1058 bitmap->texture_masked = NULL;
1061 void SDLBlitSurface(SDL_Surface *src_surface, SDL_Surface *dst_surface,
1062 int src_x, int src_y, int width, int height,
1063 int dst_x, int dst_y)
1065 SDL_Rect src_rect, dst_rect;
1070 src_rect.h = height;
1075 dst_rect.h = height;
1077 SDL_BlitSurface(src_surface, &src_rect, dst_surface, &dst_rect);
1080 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1081 int src_x, int src_y, int width, int height,
1082 int dst_x, int dst_y, int mask_mode)
1084 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1085 SDL_Rect src_rect, dst_rect;
1090 src_rect.h = height;
1095 dst_rect.h = height;
1097 SDLSetBitmapAlpha(src_bitmap, FALSE, mask_mode == BLIT_MASKED);
1099 // if (src_bitmap != backbuffer || dst_bitmap != window)
1100 if (!(src_bitmap == backbuffer && dst_bitmap == window))
1101 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
1102 src_bitmap->surface_masked : src_bitmap->surface),
1103 &src_rect, real_dst_bitmap->surface, &dst_rect);
1105 if (dst_bitmap == window)
1106 UpdateScreen_WithFrameDelay(&dst_rect);
1109 void SDLBlitTexture(Bitmap *bitmap,
1110 int src_x, int src_y, int width, int height,
1111 int dst_x, int dst_y, int mask_mode)
1113 SDL_Texture *texture;
1118 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1120 if (texture == NULL)
1126 src_rect.h = height;
1131 dst_rect.h = height;
1133 SDLSetBitmapAlpha(bitmap, TRUE, mask_mode == BLIT_MASKED);
1135 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1138 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1141 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1149 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1151 if (dst_bitmap == window)
1152 UpdateScreen_WithFrameDelay(&rect);
1155 void PrepareFadeBitmap(int draw_target)
1157 Bitmap *fade_bitmap =
1158 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1159 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1161 if (fade_bitmap == NULL)
1164 // copy backbuffer to fading buffer
1165 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1167 // add border and animations to fading buffer
1168 FinalizeScreen(draw_target);
1171 void SDLFadeRectangle(int x, int y, int width, int height,
1172 int fade_mode, int fade_delay, int post_delay,
1173 void (*draw_border_function)(void))
1175 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1176 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1177 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1178 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1179 SDL_Surface *surface_screen = backbuffer->surface;
1180 SDL_Rect src_rect, dst_rect;
1182 int src_x = x, src_y = y;
1183 int dst_x = x, dst_y = y;
1184 unsigned int time_last, time_current;
1186 // store function for drawing global masked border
1187 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1189 // deactivate drawing of global border while fading, if needed
1190 if (draw_border_function == NULL)
1191 gfx.draw_global_border_function = NULL;
1196 src_rect.h = height;
1200 dst_rect.w = width; // (ignored)
1201 dst_rect.h = height; // (ignored)
1203 dst_rect2 = dst_rect;
1205 // before fading in, store backbuffer (without animation graphics)
1206 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1207 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1209 // copy source and target surfaces to temporary surfaces for fading
1210 if (fade_mode & FADE_TYPE_TRANSFORM)
1212 // (source and target fading buffer already prepared)
1214 else if (fade_mode & FADE_TYPE_FADE_IN)
1216 // (target fading buffer already prepared)
1217 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1219 else // FADE_TYPE_FADE_OUT
1221 // (source fading buffer already prepared)
1222 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1225 time_current = SDL_GetTicks();
1227 if (fade_delay <= 0)
1229 // immediately draw final target frame without delay
1230 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1234 // when fading without delay, also skip post delay
1238 if (fade_mode == FADE_MODE_MELT)
1240 boolean done = FALSE;
1241 int melt_pixels = 2;
1242 int melt_columns = width / melt_pixels;
1243 int ypos[melt_columns];
1244 int max_steps = height / 8 + 32;
1249 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1251 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1253 ypos[0] = -GetSimpleRandom(16);
1255 for (i = 1 ; i < melt_columns; i++)
1257 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1259 ypos[i] = ypos[i - 1] + r;
1272 time_last = time_current;
1273 time_current = SDL_GetTicks();
1274 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1275 steps_final = MIN(MAX(0, steps), max_steps);
1279 done = (steps_done >= steps_final);
1281 for (i = 0 ; i < melt_columns; i++)
1289 else if (ypos[i] < height)
1294 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1296 if (ypos[i] + dy >= height)
1297 dy = height - ypos[i];
1299 // copy part of (appearing) target surface to upper area
1300 src_rect.x = src_x + i * melt_pixels;
1301 // src_rect.y = src_y + ypos[i];
1303 src_rect.w = melt_pixels;
1305 src_rect.h = ypos[i] + dy;
1307 dst_rect.x = dst_x + i * melt_pixels;
1308 // dst_rect.y = dst_y + ypos[i];
1311 if (steps_done >= steps_final)
1312 SDL_BlitSurface(surface_target, &src_rect,
1313 surface_screen, &dst_rect);
1317 // copy part of (disappearing) source surface to lower area
1318 src_rect.x = src_x + i * melt_pixels;
1320 src_rect.w = melt_pixels;
1321 src_rect.h = height - ypos[i];
1323 dst_rect.x = dst_x + i * melt_pixels;
1324 dst_rect.y = dst_y + ypos[i];
1326 if (steps_done >= steps_final)
1327 SDL_BlitSurface(surface_source, &src_rect,
1328 surface_screen, &dst_rect);
1334 src_rect.x = src_x + i * melt_pixels;
1336 src_rect.w = melt_pixels;
1337 src_rect.h = height;
1339 dst_rect.x = dst_x + i * melt_pixels;
1342 if (steps_done >= steps_final)
1343 SDL_BlitSurface(surface_target, &src_rect,
1344 surface_screen, &dst_rect);
1348 if (steps_done >= steps_final)
1350 if (draw_border_function != NULL)
1351 draw_border_function();
1353 UpdateScreen_WithFrameDelay(&dst_rect2);
1355 if (PendingEscapeKeyEvent())
1360 else if (fade_mode == FADE_MODE_CURTAIN)
1364 int xx_size = width / 2;
1366 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1368 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1370 for (xx = 0; xx < xx_size;)
1372 time_last = time_current;
1373 time_current = SDL_GetTicks();
1374 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1375 xx_final = MIN(MAX(0, xx), xx_size);
1380 src_rect.h = height;
1385 // draw new (target) image to screen buffer
1386 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1388 if (xx_final < xx_size)
1390 src_rect.w = xx_size - xx_final;
1391 src_rect.h = height;
1393 // draw old (source) image to screen buffer (left side)
1395 src_rect.x = src_x + xx_final;
1398 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1400 // draw old (source) image to screen buffer (right side)
1402 src_rect.x = src_x + xx_size;
1403 dst_rect.x = dst_x + xx_size + xx_final;
1405 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1408 if (draw_border_function != NULL)
1409 draw_border_function();
1411 // only update the region of the screen that is affected from fading
1412 UpdateScreen_WithFrameDelay(&dst_rect2);
1414 if (PendingEscapeKeyEvent())
1418 else // fading in, fading out or cross-fading
1423 for (alpha = 0.0; alpha < 255.0;)
1425 time_last = time_current;
1426 time_current = SDL_GetTicks();
1427 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1428 alpha_final = MIN(MAX(0, alpha), 255);
1430 // draw existing (source) image to screen buffer
1431 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1433 // draw new (target) image to screen buffer using alpha blending
1434 SDLSetAlpha(surface_target, TRUE, alpha_final);
1435 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1437 if (draw_border_function != NULL)
1438 draw_border_function();
1440 // only update the region of the screen that is affected from fading
1441 UpdateScreen_WithFrameDelay(&dst_rect);
1443 if (PendingEscapeKeyEvent())
1449 Delay_WithScreenUpdates(post_delay);
1451 // restore function for drawing global masked border
1452 gfx.draw_global_border_function = draw_global_border_function;
1454 // after fading in, restore backbuffer (without animation graphics)
1455 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1456 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1459 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1460 int to_x, int to_y, Uint32 color)
1462 SDL_Surface *surface = dst_bitmap->surface;
1466 swap_numbers(&from_x, &to_x);
1469 swap_numbers(&from_y, &to_y);
1473 rect.w = (to_x - from_x + 1);
1474 rect.h = (to_y - from_y + 1);
1476 SDL_FillRect(surface, &rect, color);
1479 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1480 int to_x, int to_y, Uint32 color)
1482 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1485 #if ENABLE_UNUSED_CODE
1486 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1487 int num_points, Uint32 color)
1492 for (i = 0; i < num_points - 1; i++)
1494 for (x = 0; x < line_width; x++)
1496 for (y = 0; y < line_width; y++)
1498 int dx = x - line_width / 2;
1499 int dy = y - line_width / 2;
1501 if ((x == 0 && y == 0) ||
1502 (x == 0 && y == line_width - 1) ||
1503 (x == line_width - 1 && y == 0) ||
1504 (x == line_width - 1 && y == line_width - 1))
1507 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1508 points[i + 1].x + dx, points[i + 1].y + dy, color);
1515 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1517 SDL_Surface *surface = src_bitmap->surface;
1519 switch (surface->format->BytesPerPixel)
1521 case 1: // assuming 8-bpp
1523 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1527 case 2: // probably 15-bpp or 16-bpp
1529 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1533 case 3: // slow 24-bpp mode; usually not used
1536 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1540 shift = surface->format->Rshift;
1541 color |= *(pix + shift / 8) >> shift;
1542 shift = surface->format->Gshift;
1543 color |= *(pix + shift / 8) >> shift;
1544 shift = surface->format->Bshift;
1545 color |= *(pix + shift / 8) >> shift;
1551 case 4: // probably 32-bpp
1553 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1562 // ============================================================================
1563 // The following functions were taken from the SGE library
1564 // (SDL Graphics Extension Library) by Anders Lindström
1565 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1566 // ============================================================================
1568 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1570 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1572 switch (surface->format->BytesPerPixel)
1577 *((Uint8 *)surface->pixels + y * surface->pitch + x) = color;
1583 // Probably 15-bpp or 16-bpp
1584 *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x) = color;
1590 // Slow 24-bpp mode, usually not used
1594 // Gack - slow, but endian correct
1595 pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1596 shift = surface->format->Rshift;
1597 *(pix + shift / 8) = color>>shift;
1598 shift = surface->format->Gshift;
1599 *(pix + shift / 8) = color>>shift;
1600 shift = surface->format->Bshift;
1601 *(pix + shift / 8) = color>>shift;
1608 *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x) = color;
1616 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1617 Uint8 R, Uint8 G, Uint8 B)
1619 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1622 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1624 *((Uint8 *)surface->pixels + y * surface->pitch + x) = color;
1627 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1629 *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x) = color;
1632 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1637 // Gack - slow, but endian correct
1638 pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1639 shift = surface->format->Rshift;
1640 *(pix + shift / 8) = color>>shift;
1641 shift = surface->format->Gshift;
1642 *(pix + shift / 8) = color>>shift;
1643 shift = surface->format->Bshift;
1644 *(pix + shift / 8) = color>>shift;
1647 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1649 *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x) = color;
1652 static void _PutPixelX(SDL_Surface *dest, Sint16 x, Sint16 y, Uint32 color)
1654 switch (dest->format->BytesPerPixel)
1657 *((Uint8 *)dest->pixels + y * dest->pitch + x) = color;
1661 *((Uint16 *)dest->pixels + y * dest->pitch / 2 + x) = color;
1665 _PutPixel24(dest, x, y, color);
1669 *((Uint32 *)dest->pixels + y * dest->pitch / 4 + x) = color;
1675 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1677 if (SDL_MUSTLOCK(surface))
1679 if (SDL_LockSurface(surface) < 0)
1685 _PutPixel(surface, x, y, color);
1687 if (SDL_MUSTLOCK(surface))
1689 SDL_UnlockSurface(surface);
1694 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1695 Uint8 r, Uint8 g, Uint8 b)
1697 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1700 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1702 if (y >= 0 && y <= dest->h - 1)
1704 switch (dest->format->BytesPerPixel)
1707 return y * dest->pitch;
1711 return y * dest->pitch / 2;
1715 return y * dest->pitch;
1719 return y * dest->pitch / 4;
1727 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1730 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1732 switch (surface->format->BytesPerPixel)
1737 *((Uint8 *)surface->pixels + ypitch + x) = color;
1743 // Probably 15-bpp or 16-bpp
1744 *((Uint16 *)surface->pixels + ypitch + x) = color;
1750 // Slow 24-bpp mode, usually not used
1754 // Gack - slow, but endian correct
1755 pix = (Uint8 *)surface->pixels + ypitch + x * 3;
1756 shift = surface->format->Rshift;
1757 *(pix + shift / 8) = color>>shift;
1758 shift = surface->format->Gshift;
1759 *(pix + shift / 8) = color>>shift;
1760 shift = surface->format->Bshift;
1761 *(pix + shift / 8) = color>>shift;
1768 *((Uint32 *)surface->pixels + ypitch + x) = color;
1775 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1780 if (SDL_MUSTLOCK(Surface))
1782 if (SDL_LockSurface(Surface) < 0)
1796 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1800 if (x2 > Surface->w - 1)
1801 x2 = Surface->w - 1;
1808 SDL_FillRect(Surface, &l, Color);
1810 if (SDL_MUSTLOCK(Surface))
1812 SDL_UnlockSurface(Surface);
1816 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1817 Uint8 R, Uint8 G, Uint8 B)
1819 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1822 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1835 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1839 if (x2 > Surface->w - 1)
1840 x2 = Surface->w - 1;
1847 SDL_FillRect(Surface, &l, Color);
1850 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1855 if (SDL_MUSTLOCK(Surface))
1857 if (SDL_LockSurface(Surface) < 0)
1871 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1875 if (y2 > Surface->h - 1)
1876 y2 = Surface->h - 1;
1883 SDL_FillRect(Surface, &l, Color);
1885 if (SDL_MUSTLOCK(Surface))
1887 SDL_UnlockSurface(Surface);
1891 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1892 Uint8 R, Uint8 G, Uint8 B)
1894 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1897 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1910 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1914 if (y2 > Surface->h - 1)
1915 y2 = Surface->h - 1;
1922 SDL_FillRect(Surface, &l, Color);
1926 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1927 Sint16 x2, Sint16 y2, Uint32 Color,
1928 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1931 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1936 sdx = (dx < 0) ? -1 : 1;
1937 sdy = (dy < 0) ? -1 : 1;
1949 for (x = 0; x < dx; x++)
1951 Callback(Surface, px, py, Color);
1965 for (y = 0; y < dy; y++)
1967 Callback(Surface, px, py, Color);
1982 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1983 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1984 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1987 sge_DoLine(Surface, X1, Y1, X2, Y2,
1988 SDL_MapRGB(Surface->format, R, G, B), Callback);
1992 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1995 if (SDL_MUSTLOCK(Surface))
1997 if (SDL_LockSurface(Surface) < 0)
2002 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
2004 // unlock the display
2005 if (SDL_MUSTLOCK(Surface))
2007 SDL_UnlockSurface(Surface);
2012 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
2013 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
2015 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
2019 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
2021 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
2025 // ----------------------------------------------------------------------------
2026 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
2027 // ----------------------------------------------------------------------------
2029 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
2030 int src_x, int src_y, int width, int height,
2031 int dst_x, int dst_y)
2035 for (y = 0; y < height; y++)
2037 for (x = 0; x < width; x++)
2039 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
2041 if (pixel != BLACK_PIXEL)
2042 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
2048 // ============================================================================
2049 // The following functions were taken from the SDL_gfx library version 2.0.3
2050 // (Rotozoomer) by Andreas Schiffler
2051 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
2052 // ============================================================================
2054 // ----------------------------------------------------------------------------
2057 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
2058 // ----------------------------------------------------------------------------
2068 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
2071 tColorRGBA *sp, *csp, *dp;
2075 sp = csp = (tColorRGBA *) src->pixels;
2076 dp = (tColorRGBA *) dst->pixels;
2077 dgap = dst->pitch - dst->w * 4;
2079 for (y = 0; y < dst->h; y++)
2083 for (x = 0; x < dst->w; x++)
2085 tColorRGBA *sp0 = sp;
2086 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
2087 tColorRGBA *sp00 = &sp0[0];
2088 tColorRGBA *sp01 = &sp0[1];
2089 tColorRGBA *sp10 = &sp1[0];
2090 tColorRGBA *sp11 = &sp1[1];
2093 // create new color pixel from all four source color pixels
2094 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
2095 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
2096 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
2097 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
2102 // advance source pointers
2105 // advance destination pointer
2109 // advance source pointer
2110 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2112 // advance destination pointers
2113 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2119 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2121 int x, y, *sax, *say, *csax, *csay;
2123 tColorRGBA *sp, *csp, *csp0, *dp;
2126 // use specialized zoom function when scaling down to exactly half size
2127 if (src->w == 2 * dst->w &&
2128 src->h == 2 * dst->h)
2129 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2132 sx = (float) src->w / (float) dst->w;
2133 sy = (float) src->h / (float) dst->h;
2135 // allocate memory for row increments
2136 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2137 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2139 // precalculate row increments
2140 for (x = 0; x <= dst->w; x++)
2141 *csax++ = (int)(sx * x);
2143 for (y = 0; y <= dst->h; y++)
2144 *csay++ = (int)(sy * y);
2147 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2148 dp = (tColorRGBA *) dst->pixels;
2149 dgap = dst->pitch - dst->w * 4;
2152 for (y = 0; y < dst->h; y++)
2157 for (x = 0; x < dst->w; x++)
2162 // advance source pointers
2166 // advance destination pointer
2170 // advance source pointer
2172 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2174 // advance destination pointers
2175 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2184 // ----------------------------------------------------------------------------
2187 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2188 // ----------------------------------------------------------------------------
2190 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2192 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2193 Uint8 *sp, *dp, *csp;
2197 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2198 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2200 // allocate memory for row increments
2201 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2202 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2204 // precalculate row increments
2207 for (x = 0; x < dst->w; x++)
2210 *csax = (csx >> 16);
2217 for (y = 0; y < dst->h; y++)
2220 *csay = (csy >> 16);
2227 for (x = 0; x < dst->w; x++)
2235 for (y = 0; y < dst->h; y++)
2242 sp = csp = (Uint8 *) src->pixels;
2243 dp = (Uint8 *) dst->pixels;
2244 dgap = dst->pitch - dst->w;
2248 for (y = 0; y < dst->h; y++)
2252 for (x = 0; x < dst->w; x++)
2257 // advance source pointers
2261 // advance destination pointer
2265 // advance source pointer (for row)
2266 csp += ((*csay) * src->pitch);
2269 // advance destination pointers
2279 // ----------------------------------------------------------------------------
2282 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2283 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2284 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2285 // into a 32bit RGBA format on the fly.
2286 // ----------------------------------------------------------------------------
2288 SDL_Surface *SDLZoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2290 SDL_Surface *zoom_src = NULL;
2291 SDL_Surface *zoom_dst = NULL;
2292 boolean is_converted = FALSE;
2299 // determine if source surface is 32 bit or 8 bit
2300 is_32bit = (src->format->BitsPerPixel == 32);
2302 if (is_32bit || src->format->BitsPerPixel == 8)
2304 // use source surface 'as is'
2309 // new source surface is 32 bit with a defined RGB ordering
2310 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2311 0x000000ff, 0x0000ff00, 0x00ff0000,
2312 (src->format->Amask ? 0xff000000 : 0));
2313 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2315 is_converted = TRUE;
2318 // allocate surface to completely contain the zoomed surface
2321 // target surface is 32 bit with source RGBA/ABGR ordering
2322 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2323 zoom_src->format->Rmask,
2324 zoom_src->format->Gmask,
2325 zoom_src->format->Bmask,
2326 zoom_src->format->Amask);
2330 // target surface is 8 bit
2331 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2335 // lock source surface
2336 SDL_LockSurface(zoom_src);
2338 // check which kind of surface we have
2341 // call the 32 bit transformation routine to do the zooming
2342 zoomSurfaceRGBA(zoom_src, zoom_dst);
2347 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2348 zoom_dst->format->palette->colors[i] =
2349 zoom_src->format->palette->colors[i];
2350 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2352 // call the 8 bit transformation routine to do the zooming
2353 zoomSurfaceY(zoom_src, zoom_dst);
2356 // unlock source surface
2357 SDL_UnlockSurface(zoom_src);
2359 // free temporary surface
2361 SDL_FreeSurface(zoom_src);
2363 // return destination surface
2367 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2369 Bitmap *dst_bitmap = CreateBitmapStruct();
2370 SDL_Surface *src_surface = src_bitmap->surface_masked;
2371 SDL_Surface *dst_surface;
2373 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2374 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2376 dst_bitmap->width = dst_width;
2377 dst_bitmap->height = dst_height;
2379 // create zoomed temporary surface from source surface
2380 dst_surface = SDLZoomSurface(src_surface, dst_width, dst_height);
2382 // create native format destination surface from zoomed temporary surface
2383 SDLSetNativeSurface(&dst_surface);
2385 // set color key for zoomed surface from source surface, if defined
2386 if (SDLHasColorKey(src_surface))
2387 SDLCopyColorKey(src_surface, dst_surface);
2389 // create native non-transparent surface for opaque blitting
2390 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2392 // set native transparent surface for masked blitting
2393 dst_bitmap->surface_masked = dst_surface;
2399 // ============================================================================
2400 // load image to bitmap
2401 // ============================================================================
2403 Bitmap *SDLLoadImage(char *filename)
2405 Bitmap *new_bitmap = CreateBitmapStruct();
2406 SDL_Surface *sdl_image_tmp;
2408 if (program.headless)
2410 // prevent sanity check warnings at later stage
2411 new_bitmap->width = new_bitmap->height = 1;
2416 print_timestamp_init("SDLLoadImage");
2418 print_timestamp_time(getBaseNamePtr(filename));
2420 // load image to temporary surface
2421 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2422 Fail("IMG_Load('%s') failed: %s", getBaseNamePtr(filename), SDL_GetError());
2424 print_timestamp_time("IMG_Load");
2426 UPDATE_BUSY_STATE();
2428 // create native non-transparent surface for current image
2429 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2430 Fail("SDLGetOpaqueSurface() failed");
2432 print_timestamp_time("SDLGetNativeSurface (opaque)");
2434 UPDATE_BUSY_STATE();
2436 // set black pixel to transparent if no alpha channel / transparent color
2437 if (!SDLHasAlpha(sdl_image_tmp) &&
2438 !SDLHasColorKey(sdl_image_tmp))
2439 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2440 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2442 // create native transparent surface for current image
2443 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2444 Fail("SDLGetNativeSurface() failed");
2446 print_timestamp_time("SDLGetNativeSurface (masked)");
2448 UPDATE_BUSY_STATE();
2450 // free temporary surface
2451 SDL_FreeSurface(sdl_image_tmp);
2453 new_bitmap->width = new_bitmap->surface->w;
2454 new_bitmap->height = new_bitmap->surface->h;
2456 print_timestamp_done("SDLLoadImage");
2462 // ----------------------------------------------------------------------------
2463 // custom cursor fuctions
2464 // ----------------------------------------------------------------------------
2466 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2468 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2469 cursor_info->width, cursor_info->height,
2470 cursor_info->hot_x, cursor_info->hot_y);
2473 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2475 static struct MouseCursorInfo *last_cursor_info = NULL;
2476 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2477 static SDL_Cursor *cursor_default = NULL;
2478 static SDL_Cursor *cursor_current = NULL;
2480 // if invoked for the first time, store the SDL default cursor
2481 if (cursor_default == NULL)
2482 cursor_default = SDL_GetCursor();
2484 // only create new cursor if cursor info (custom only) has changed
2485 if (cursor_info != NULL && cursor_info != last_cursor_info)
2487 cursor_current = create_cursor(cursor_info);
2488 last_cursor_info = cursor_info;
2491 // only set new cursor if cursor info (custom or NULL) has changed
2492 if (cursor_info != last_cursor_info2)
2493 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2495 last_cursor_info2 = cursor_info;
2499 // ============================================================================
2501 // ============================================================================
2503 void SDLOpenAudio(void)
2505 if (program.headless)
2508 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2510 Warn("SDL_InitSubSystem() failed: %s", SDL_GetError());
2515 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2516 AUDIO_NUM_CHANNELS_STEREO,
2517 setup.system.audio_fragment_size) < 0)
2519 Warn("Mix_OpenAudio() failed: %s", SDL_GetError());
2524 audio.sound_available = TRUE;
2525 audio.music_available = TRUE;
2526 audio.loops_available = TRUE;
2527 audio.sound_enabled = TRUE;
2529 // set number of available mixer channels
2530 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2531 audio.music_channel = MUSIC_CHANNEL;
2532 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2534 Mixer_InitChannels();
2537 void SDLCloseAudio(void)
2540 Mix_HaltChannel(-1);
2543 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2547 // ============================================================================
2549 // ============================================================================
2551 void SDLWaitEvent(Event *event)
2553 SDL_WaitEvent(event);
2556 void SDLCorrectRawMousePosition(int *x, int *y)
2558 if (sdl_renderer == NULL)
2561 // this corrects the raw mouse position for logical screen size within event
2562 // filters (correction done later by SDL library when handling mouse events)
2565 float scale_x, scale_y;
2567 SDL_RenderGetViewport(sdl_renderer, &viewport);
2568 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2570 *x = (int)(*x / scale_x);
2571 *y = (int)(*y / scale_y);
2578 // ============================================================================
2579 // joystick functions
2580 // ============================================================================
2582 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2583 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2584 static int sdl_js_axis[MAX_PLAYERS][2];
2585 static int sdl_js_button[MAX_PLAYERS][2];
2586 static boolean sdl_is_controller[MAX_PLAYERS];
2588 void SDLClearJoystickState(void)
2592 for (i = 0; i < MAX_PLAYERS; i++)
2594 for (j = 0; j < 2; j++)
2596 sdl_js_axis_raw[i][j] = -1;
2597 sdl_js_axis[i][j] = 0;
2598 sdl_js_button[i][j] = 0;
2603 boolean SDLOpenJoystick(int nr)
2605 if (nr < 0 || nr >= MAX_PLAYERS)
2608 sdl_is_controller[nr] = SDL_IsGameController(nr);
2611 Debug("joystick", "opening joystick %d (%s)",
2612 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2615 if (sdl_is_controller[nr])
2616 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2618 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2620 return (sdl_joystick[nr] != NULL);
2623 void SDLCloseJoystick(int nr)
2625 if (nr < 0 || nr >= MAX_PLAYERS)
2629 Debug("joystick", "closing joystick %d", nr);
2632 if (sdl_is_controller[nr])
2633 SDL_GameControllerClose(sdl_joystick[nr]);
2635 SDL_JoystickClose(sdl_joystick[nr]);
2637 sdl_joystick[nr] = NULL;
2640 boolean SDLCheckJoystickOpened(int nr)
2642 if (nr < 0 || nr >= MAX_PLAYERS)
2645 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2648 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2650 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2651 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2652 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2653 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2655 if (nr < 0 || nr >= MAX_PLAYERS)
2661 // prevent (slightly jittering, but centered) axis A from resetting axis B
2662 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2663 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2666 sdl_js_axis[nr][axis_id] = axis_value;
2667 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2670 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2672 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2673 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2674 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2675 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2676 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2677 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2678 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2679 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2682 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2683 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2684 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2685 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2686 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2687 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2688 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2689 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2691 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2692 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2693 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2694 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2695 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2697 if (nr < 0 || nr >= MAX_PLAYERS)
2700 if (button_id == -1)
2703 sdl_js_button[nr][button_id] = button_state;
2706 void HandleJoystickEvent(Event *event)
2708 // when using joystick, disable overlay touch buttons
2709 runtime.uses_touch_device = FALSE;
2711 switch (event->type)
2713 case SDL_CONTROLLERDEVICEADDED:
2715 Debug("joystick", "SDL_CONTROLLERDEVICEADDED: device %d added",
2716 event->cdevice.which);
2721 case SDL_CONTROLLERDEVICEREMOVED:
2723 Debug("joystick", "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2724 event->cdevice.which);
2729 case SDL_CONTROLLERAXISMOTION:
2731 Debug("joystick", "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2732 event->caxis.which, event->caxis.axis, event->caxis.value);
2734 setJoystickAxis(event->caxis.which,
2736 event->caxis.value);
2739 case SDL_CONTROLLERBUTTONDOWN:
2741 Debug("joystick", "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2742 event->cbutton.which, event->cbutton.button);
2744 setJoystickButton(event->cbutton.which,
2745 event->cbutton.button,
2749 case SDL_CONTROLLERBUTTONUP:
2751 Debug("joystick", "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2752 event->cbutton.which, event->cbutton.button);
2754 setJoystickButton(event->cbutton.which,
2755 event->cbutton.button,
2759 case SDL_JOYAXISMOTION:
2760 if (sdl_is_controller[event->jaxis.which])
2764 Debug("joystick", "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2765 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2767 if (event->jaxis.axis < 4)
2768 setJoystickAxis(event->jaxis.which,
2770 event->jaxis.value);
2773 case SDL_JOYBUTTONDOWN:
2774 if (sdl_is_controller[event->jaxis.which])
2778 Debug("joystick", "SDL_JOYBUTTONDOWN: device %d, button %d",
2779 event->jbutton.which, event->jbutton.button);
2781 if (event->jbutton.button < 4)
2782 setJoystickButton(event->jbutton.which,
2783 event->jbutton.button,
2787 case SDL_JOYBUTTONUP:
2788 if (sdl_is_controller[event->jaxis.which])
2792 Debug("joystick", "SDL_JOYBUTTONUP: device %d, button %d",
2793 event->jbutton.which, event->jbutton.button);
2795 if (event->jbutton.button < 4)
2796 setJoystickButton(event->jbutton.which,
2797 event->jbutton.button,
2806 void SDLInitJoysticks(void)
2808 static boolean sdl_joystick_subsystem_initialized = FALSE;
2809 boolean print_warning = !sdl_joystick_subsystem_initialized;
2810 char *mappings_file_base = getPath2(options.conf_directory,
2811 GAMECONTROLLER_BASENAME);
2812 char *mappings_file_user = getPath2(getMainUserGameDataDir(),
2813 GAMECONTROLLER_BASENAME);
2817 if (!sdl_joystick_subsystem_initialized)
2819 sdl_joystick_subsystem_initialized = TRUE;
2821 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2823 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2824 Fail("SDL_Init() failed: %s", SDL_GetError());
2826 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2828 // the included game controller base mappings should always be found
2829 if (num_mappings == -1)
2830 Warn("no game controller base mappings found");
2833 Debug("joystick", "%d game controller base mapping(s) added",
2837 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2840 // the personal game controller user mappings may or may not be found
2841 if (num_mappings == -1)
2842 Warn("no game controller user mappings found");
2844 Debug("joystick", , "%d game controller user mapping(s) added",
2847 Debug("joystick", "%d joystick(s) found:", SDL_NumJoysticks());
2850 checked_free(mappings_file_base);
2851 checked_free(mappings_file_user);
2854 for (i = 0; i < SDL_NumJoysticks(); i++)
2856 const char *name, *type;
2858 if (SDL_IsGameController(i))
2860 name = SDL_GameControllerNameForIndex(i);
2861 type = "game controller";
2865 name = SDL_JoystickNameForIndex(i);
2869 Debug("joystick", "- joystick %d (%s): '%s'",
2870 i, type, (name ? name : "(Unknown)"));
2875 // assign joysticks from configured to connected joystick for all players
2876 for (i = 0; i < MAX_PLAYERS; i++)
2878 // get configured joystick for this player
2879 char *device_name = setup.input[i].joy.device_name;
2880 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2882 if (joystick_nr >= SDL_NumJoysticks())
2884 if (setup.input[i].use_joystick && print_warning)
2885 Warn("cannot find joystick %d", joystick_nr);
2890 // store configured joystick number for each player
2891 joystick.nr[i] = joystick_nr;
2894 // now open all connected joysticks (regardless if configured or not)
2895 for (i = 0; i < SDL_NumJoysticks(); i++)
2897 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2898 if (SDLCheckJoystickOpened(i))
2899 SDLCloseJoystick(i);
2901 if (SDLOpenJoystick(i))
2902 joystick.status = JOYSTICK_ACTIVATED;
2903 else if (print_warning)
2904 Warn("cannot open joystick %d", i);
2907 SDLClearJoystickState();
2910 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2912 if (nr < 0 || nr >= MAX_PLAYERS)
2916 *x = sdl_js_axis[nr][0];
2918 *y = sdl_js_axis[nr][1];
2921 *b1 = sdl_js_button[nr][0];
2923 *b2 = sdl_js_button[nr][1];
2929 // ============================================================================
2930 // touch input overlay functions
2931 // ============================================================================
2933 #if defined(USE_TOUCH_INPUT_OVERLAY)
2934 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2937 int grid_xsize = overlay.grid_xsize;
2938 int grid_ysize = overlay.grid_ysize;
2941 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2942 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2944 for (x = 0; x < grid_xsize; x++)
2946 rect.x = (x + 0) * video.screen_width / grid_xsize;
2947 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2949 for (y = 0; y < grid_ysize; y++)
2951 rect.y = (y + 0) * video.screen_height / grid_ysize;
2952 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2954 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2955 SDL_RenderDrawRect(sdl_renderer, &rect);
2959 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2962 static void RenderFillRectangle(int x, int y, int width, int height)
2964 SDL_Rect rect = { x, y, width, height };
2966 SDL_RenderFillRect(sdl_renderer, &rect);
2969 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2971 static int alpha_direction = 0;
2972 static int alpha_highlight = 0;
2973 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2974 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2976 int grid_xsize = overlay.grid_xsize;
2977 int grid_ysize = overlay.grid_ysize;
2980 if (alpha == alpha_max)
2982 if (alpha_direction < 0)
2984 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2986 if (alpha_highlight == 0)
2987 alpha_direction = 1;
2991 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2993 if (alpha_highlight == alpha_max)
2994 alpha_direction = -1;
2999 alpha_direction = 1;
3000 alpha_highlight = alpha;
3003 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
3005 for (x = 0; x < grid_xsize; x++)
3007 for (y = 0; y < grid_ysize; y++)
3009 int grid_button = overlay.grid_button[x][y];
3010 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
3011 int alpha_draw = alpha;
3012 int outline_border = MV_NONE;
3013 int border_size = 2;
3014 boolean draw_outlined = setup.touch.draw_outlined;
3015 boolean draw_pressed = setup.touch.draw_pressed;
3017 if (grid_button == CHAR_GRID_BUTTON_NONE)
3020 if (grid_button == overlay.grid_button_highlight)
3022 draw_outlined = FALSE;
3023 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
3026 if (draw_pressed && overlay.grid_button_action & grid_button_action)
3029 draw_outlined = FALSE;
3031 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
3034 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
3036 rect.x = (x + 0) * video.screen_width / grid_xsize;
3037 rect.y = (y + 0) * video.screen_height / grid_ysize;
3038 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
3039 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
3041 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
3043 rect.x += border_size;
3044 rect.w -= border_size;
3046 outline_border |= MV_LEFT;
3049 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
3051 rect.w -= border_size;
3053 outline_border |= MV_RIGHT;
3056 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
3058 rect.y += border_size;
3059 rect.h -= border_size;
3061 outline_border |= MV_UP;
3064 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
3066 rect.h -= border_size;
3068 outline_border |= MV_DOWN;
3073 int rect_x = rect.x +
3074 (outline_border & MV_LEFT ? border_size : 0);
3075 int rect_w = rect.w -
3076 (outline_border & MV_LEFT ? border_size : 0) -
3077 (outline_border & MV_RIGHT ? border_size : 0);
3079 if (outline_border & MV_LEFT)
3080 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
3082 if (outline_border & MV_RIGHT)
3083 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
3084 border_size, rect.h);
3086 if (outline_border & MV_UP)
3087 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
3089 if (outline_border & MV_DOWN)
3090 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
3091 rect_w, border_size);
3095 SDL_RenderFillRect(sdl_renderer, &rect);
3100 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3103 static void DrawTouchInputOverlay(void)
3105 static boolean deactivated = TRUE;
3106 static boolean show_grid = FALSE;
3107 static int alpha = 0;
3108 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3109 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3110 boolean active = (overlay.enabled && overlay.active);
3112 if (!active && deactivated)
3117 if (alpha < alpha_max)
3118 alpha = MIN(alpha + alpha_step, alpha_max);
3120 deactivated = FALSE;
3124 alpha = MAX(0, alpha - alpha_step);
3130 if (overlay.show_grid)
3132 else if (deactivated)
3136 DrawTouchInputOverlay_ShowGrid(alpha);
3138 DrawTouchInputOverlay_ShowGridButtons(alpha);
3141 static void DrawTouchGadgetsOverlay(void)
3143 DrawGadgets_OverlayTouchButtons();