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_WithMaskedColor(SDL_Surface *surface, int r, int g, int b)
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, r, g, b));
530 Bitmap *SDLGetBitmapFromSurface(SDL_Surface *surface)
532 return SDLGetBitmapFromSurface_WithMaskedColor(surface, 0x00, 0x00, 0x00);
535 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
537 if (program.headless)
540 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
543 Fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
548 void SDLCreateBitmapTextures(Bitmap *bitmap)
554 SDL_DestroyTexture(bitmap->texture);
555 if (bitmap->texture_masked)
556 SDL_DestroyTexture(bitmap->texture_masked);
558 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
559 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
562 void SDLFreeBitmapTextures(Bitmap *bitmap)
568 SDL_DestroyTexture(bitmap->texture);
569 if (bitmap->texture_masked)
570 SDL_DestroyTexture(bitmap->texture_masked);
572 bitmap->texture = NULL;
573 bitmap->texture_masked = NULL;
576 void SDLInitVideoDisplay(void)
578 // set hint to select render driver as specified in setup config file
579 if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
580 SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
582 // initialize SDL video
583 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
584 Fail("SDL_InitSubSystem() failed: %s", SDL_GetError());
586 // set default SDL depth
587 video.default_depth = 32; // (how to determine video depth in SDL2?)
589 // Code used with SDL 1.2:
590 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
593 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
595 if (program.headless)
598 video.window_scaling_percent = setup.window_scaling_percent;
599 video.window_scaling_quality = setup.window_scaling_quality;
601 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
603 // SDL 2.0: support for (desktop) fullscreen mode available
604 video.fullscreen_available = TRUE;
606 // open SDL video output device (window or fullscreen mode)
607 if (!SDLSetVideoMode(fullscreen))
608 Fail("setting video mode failed");
610 // !!! SDL2 can only set the window icon if the window already exists !!!
612 SDLSetWindowIcon(program.icon_filename);
614 // set window and icon title
618 static void SDLInitVideoBuffer_DrawBuffer(void)
620 /* SDL cannot directly draw to the visible video framebuffer like X11,
621 but always uses a backbuffer, which is then blitted to the visible
622 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
623 visible video framebuffer with 'SDL_Flip', if the hardware supports
624 this). Therefore do not use an additional backbuffer for drawing, but
625 use a symbolic buffer (distinguishable from the SDL backbuffer) called
626 'window', which indicates that the SDL backbuffer should be updated to
627 the visible video framebuffer when attempting to blit to it.
629 For convenience, it seems to be a good idea to create this symbolic
630 buffer 'window' at the same size as the SDL backbuffer. Although it
631 should never be drawn to directly, it would do no harm nevertheless. */
633 // create additional (symbolic) buffer for double-buffering
634 ReCreateBitmap(&window, video.width, video.height);
636 // create dummy drawing buffer for headless mode, if needed
637 if (program.headless)
638 ReCreateBitmap(&backbuffer, video.width, video.height);
641 void SDLInitVideoBuffer(boolean fullscreen)
643 SDLInitVideoBuffer_VideoBuffer(fullscreen);
644 SDLInitVideoBuffer_DrawBuffer();
647 static boolean SDLCreateScreen(boolean fullscreen)
649 SDL_Surface *new_surface = NULL;
651 int surface_flags_window = SURFACE_FLAGS;
652 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
655 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
657 video.vsync_mode = VSYNC_MODE_OFF;
659 if (!strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF))
661 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
662 video.vsync_mode = VSYNC_MODE_NORMAL;
665 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
666 _without_ enabling 2D/3D acceleration and/or guest additions installed,
667 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
668 it will try to use accelerated graphics and apparently fails miserably) */
669 int renderer_flags = SDL_RENDERER_SOFTWARE;
672 int width = video.width;
673 int height = video.height;
674 int screen_width = video.screen_width;
675 int screen_height = video.screen_height;
676 int surface_flags = (fullscreen ? surface_flags_fullscreen :
677 surface_flags_window);
678 int display_nr = options.display_nr;
680 // default window size is unscaled
681 video.window_width = screen_width;
682 video.window_height = screen_height;
684 // store if initial screen mode is fullscreen mode when changing screen size
685 video.fullscreen_initial = fullscreen;
687 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
689 video.window_width = window_scaling_factor * screen_width;
690 video.window_height = window_scaling_factor * screen_height;
692 if (sdl_texture_stream)
694 SDL_DestroyTexture(sdl_texture_stream);
695 sdl_texture_stream = NULL;
698 if (sdl_texture_target)
700 SDL_DestroyTexture(sdl_texture_target);
701 sdl_texture_target = NULL;
704 if (!(fullscreen && fullscreen_enabled))
708 SDL_DestroyRenderer(sdl_renderer);
714 SDL_SetWindowSize(sdl_window, video.window_width, video.window_height);
718 if (sdl_window == NULL)
719 sdl_window = SDL_CreateWindow(program.window_title,
720 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
721 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
726 if (sdl_window != NULL)
728 if (sdl_renderer == NULL)
729 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
731 if (sdl_renderer != NULL)
733 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
734 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
736 // required for setting adaptive vsync when using OpenGL renderer
737 SDLSetScreenVsyncMode(setup.vsync_mode);
739 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
740 SDL_PIXELFORMAT_ARGB8888,
741 SDL_TEXTUREACCESS_STREAMING,
744 if (SDL_RenderTargetSupported(sdl_renderer))
745 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
746 SDL_PIXELFORMAT_ARGB8888,
747 SDL_TEXTUREACCESS_TARGET,
750 if (sdl_texture_stream != NULL)
752 // use SDL default values for RGB masks and no alpha channel
753 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
755 if (new_surface == NULL)
756 Warn("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
760 Warn("SDL_CreateTexture() failed: %s", SDL_GetError());
765 Warn("SDL_CreateRenderer() failed: %s", SDL_GetError());
770 Warn("SDL_CreateWindow() failed: %s", SDL_GetError());
773 SDLSetScreenProperties();
775 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
776 if (new_surface != NULL)
777 fullscreen_enabled = fullscreen;
779 if (backbuffer == NULL)
780 backbuffer = CreateBitmapStruct();
782 backbuffer->width = video.width;
783 backbuffer->height = video.height;
785 if (backbuffer->surface)
786 SDL_FreeSurface(backbuffer->surface);
788 backbuffer->surface = new_surface;
790 return (new_surface != NULL);
793 boolean SDLSetVideoMode(boolean fullscreen)
795 boolean success = FALSE;
799 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
801 // switch display to fullscreen mode, if available
802 success = SDLCreateScreen(TRUE);
806 // switching display to fullscreen mode failed -- do not try it again
807 video.fullscreen_available = FALSE;
811 video.fullscreen_enabled = TRUE;
815 if ((!fullscreen && video.fullscreen_enabled) || !success)
817 // switch display to window mode
818 success = SDLCreateScreen(FALSE);
822 // switching display to window mode failed -- should not happen
826 video.fullscreen_enabled = FALSE;
827 video.window_scaling_percent = setup.window_scaling_percent;
828 video.window_scaling_quality = setup.window_scaling_quality;
830 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
834 SDLRedrawWindow(); // map window
839 void SDLSetWindowTitle(void)
841 if (sdl_window == NULL)
844 SDL_SetWindowTitle(sdl_window, program.window_title);
847 void SDLSetWindowScaling(int window_scaling_percent)
849 if (sdl_window == NULL)
852 float window_scaling_factor = (float)window_scaling_percent / 100;
853 int new_window_width = (int)(window_scaling_factor * video.screen_width);
854 int new_window_height = (int)(window_scaling_factor * video.screen_height);
856 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
858 video.window_scaling_percent = window_scaling_percent;
859 video.window_width = new_window_width;
860 video.window_height = new_window_height;
865 void SDLSetWindowScalingQuality(char *window_scaling_quality)
867 SDL_Texture *new_texture;
869 if (sdl_texture_stream == NULL)
872 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
874 new_texture = SDL_CreateTexture(sdl_renderer,
875 SDL_PIXELFORMAT_ARGB8888,
876 SDL_TEXTUREACCESS_STREAMING,
877 video.width, video.height);
879 if (new_texture != NULL)
881 SDL_DestroyTexture(sdl_texture_stream);
883 sdl_texture_stream = new_texture;
886 if (SDL_RenderTargetSupported(sdl_renderer))
887 new_texture = SDL_CreateTexture(sdl_renderer,
888 SDL_PIXELFORMAT_ARGB8888,
889 SDL_TEXTUREACCESS_TARGET,
890 video.width, video.height);
894 if (new_texture != NULL)
896 SDL_DestroyTexture(sdl_texture_target);
898 sdl_texture_target = new_texture;
903 video.window_scaling_quality = window_scaling_quality;
906 void SDLSetWindowFullscreen(boolean fullscreen)
908 if (sdl_window == NULL)
911 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
913 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
914 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
916 // if screen size was changed in fullscreen mode, correct desktop window size
917 if (!fullscreen && video.fullscreen_initial)
919 SDLSetWindowScaling(setup.window_scaling_percent);
920 SDL_SetWindowPosition(sdl_window,
921 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr),
922 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr));
924 video.fullscreen_initial = FALSE;
928 void SDLSetDisplaySize(void)
930 if (sdl_renderer != NULL)
934 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
936 video.display_width = w;
937 video.display_height = h;
940 Debug("video", "SDL renderer size: %d x %d",
941 video.display_width, video.display_height);
946 SDL_Rect display_bounds;
948 SDL_GetDisplayBounds(0, &display_bounds);
950 video.display_width = display_bounds.w;
951 video.display_height = display_bounds.h;
954 Debug("video", "SDL display size: %d x %d",
955 video.display_width, video.display_height);
960 void SDLSetScreenSizeAndOffsets(int width, int height)
962 // set default video screen size and offsets
963 video.screen_width = width;
964 video.screen_height = height;
965 video.screen_xoffset = 0;
966 video.screen_yoffset = 0;
968 #if defined(USE_COMPLETE_DISPLAY)
969 float ratio_video = (float) width / height;
970 float ratio_display = (float) video.display_width / video.display_height;
972 if (ratio_video != ratio_display)
974 // adjust drawable screen size to cover the whole device display
976 if (ratio_video < ratio_display)
977 video.screen_width *= ratio_display / ratio_video;
979 video.screen_height *= ratio_video / ratio_display;
981 video.screen_xoffset = (video.screen_width - width) / 2;
982 video.screen_yoffset = (video.screen_height - height) / 2;
985 Debug("video", "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
987 video.screen_width, video.screen_height,
988 ratio_video, ratio_display);
994 void SDLSetScreenSizeForRenderer(int width, int height)
996 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
999 void SDLSetScreenProperties(void)
1001 SDLSetDisplaySize();
1002 SDLSetScreenSizeAndOffsets(video.width, video.height);
1003 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
1005 SetOverlayGridSizeAndButtons();
1008 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
1010 video.screen_rendering_mode =
1011 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
1012 SPECIAL_RENDERING_BITMAP :
1013 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
1014 SPECIAL_RENDERING_TARGET:
1015 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
1016 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
1019 void SDLSetScreenVsyncMode(char *vsync_mode)
1021 // changing vsync mode without re-creating renderer only supported by OpenGL
1022 if (!strPrefixLower((char *)SDLGetRendererName(), "opengl"))
1025 int interval = VSYNC_MODE_STR_TO_INT(vsync_mode);
1026 int result = SDL_GL_SetSwapInterval(interval);
1028 // if adaptive vsync requested, but not supported, retry with normal vsync
1029 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
1031 interval = VSYNC_MODE_NORMAL;
1033 result = SDL_GL_SetSwapInterval(interval);
1037 interval = VSYNC_MODE_OFF;
1039 video.vsync_mode = interval;
1042 void SDLRedrawWindow(void)
1044 UpdateScreen_WithoutFrameDelay(NULL);
1047 void SDLFreeBitmapPointers(Bitmap *bitmap)
1049 if (bitmap->surface)
1050 SDL_FreeSurface(bitmap->surface);
1051 if (bitmap->surface_masked)
1052 SDL_FreeSurface(bitmap->surface_masked);
1054 bitmap->surface = NULL;
1055 bitmap->surface_masked = NULL;
1057 if (bitmap->texture)
1058 SDL_DestroyTexture(bitmap->texture);
1059 if (bitmap->texture_masked)
1060 SDL_DestroyTexture(bitmap->texture_masked);
1062 bitmap->texture = NULL;
1063 bitmap->texture_masked = NULL;
1066 void SDLBlitSurface(SDL_Surface *src_surface, SDL_Surface *dst_surface,
1067 int src_x, int src_y, int width, int height,
1068 int dst_x, int dst_y)
1070 SDL_Rect src_rect, dst_rect;
1075 src_rect.h = height;
1080 dst_rect.h = height;
1082 SDL_BlitSurface(src_surface, &src_rect, dst_surface, &dst_rect);
1085 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1086 int src_x, int src_y, int width, int height,
1087 int dst_x, int dst_y, int mask_mode)
1089 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1090 SDL_Rect src_rect, dst_rect;
1095 src_rect.h = height;
1100 dst_rect.h = height;
1102 SDLSetBitmapAlpha(src_bitmap, FALSE, mask_mode == BLIT_MASKED);
1104 // if (src_bitmap != backbuffer || dst_bitmap != window)
1105 if (!(src_bitmap == backbuffer && dst_bitmap == window))
1106 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
1107 src_bitmap->surface_masked : src_bitmap->surface),
1108 &src_rect, real_dst_bitmap->surface, &dst_rect);
1110 if (dst_bitmap == window)
1111 UpdateScreen_WithFrameDelay(&dst_rect);
1114 void SDLBlitTexture(Bitmap *bitmap,
1115 int src_x, int src_y, int width, int height,
1116 int dst_x, int dst_y, int mask_mode)
1118 SDL_Texture *texture;
1123 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1125 if (texture == NULL)
1131 src_rect.h = height;
1136 dst_rect.h = height;
1138 SDLSetBitmapAlpha(bitmap, TRUE, mask_mode == BLIT_MASKED);
1140 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1143 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1146 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1154 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1156 if (dst_bitmap == window)
1157 UpdateScreen_WithFrameDelay(&rect);
1160 void PrepareFadeBitmap(int draw_target)
1162 Bitmap *fade_bitmap =
1163 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1164 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1166 if (fade_bitmap == NULL)
1169 // copy backbuffer to fading buffer
1170 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1172 // add border and animations to fading buffer
1173 FinalizeScreen(draw_target);
1176 void SDLFadeRectangle(int x, int y, int width, int height,
1177 int fade_mode, int fade_delay, int post_delay,
1178 void (*draw_border_function)(void))
1180 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1181 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1182 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1183 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1184 SDL_Surface *surface_screen = backbuffer->surface;
1185 SDL_Rect src_rect, dst_rect;
1187 int src_x = x, src_y = y;
1188 int dst_x = x, dst_y = y;
1189 unsigned int time_last, time_current;
1191 // store function for drawing global masked border
1192 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1194 // deactivate drawing of global border while fading, if needed
1195 if (draw_border_function == NULL)
1196 gfx.draw_global_border_function = NULL;
1201 src_rect.h = height;
1205 dst_rect.w = width; // (ignored)
1206 dst_rect.h = height; // (ignored)
1208 dst_rect2 = dst_rect;
1210 // before fading in, store backbuffer (without animation graphics)
1211 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1212 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1214 // copy source and target surfaces to temporary surfaces for fading
1215 if (fade_mode & FADE_TYPE_TRANSFORM)
1217 // (source and target fading buffer already prepared)
1219 else if (fade_mode & FADE_TYPE_FADE_IN)
1221 // (target fading buffer already prepared)
1222 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1224 else // FADE_TYPE_FADE_OUT
1226 // (source fading buffer already prepared)
1227 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1230 time_current = SDL_GetTicks();
1232 if (fade_delay <= 0)
1234 // immediately draw final target frame without delay
1235 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1239 // when fading without delay, also skip post delay
1243 if (fade_mode == FADE_MODE_MELT)
1245 boolean done = FALSE;
1246 int melt_pixels = 2;
1247 int melt_columns = width / melt_pixels;
1248 int ypos[melt_columns];
1249 int max_steps = height / 8 + 32;
1254 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1256 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1258 ypos[0] = -GetSimpleRandom(16);
1260 for (i = 1 ; i < melt_columns; i++)
1262 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1264 ypos[i] = ypos[i - 1] + r;
1277 time_last = time_current;
1278 time_current = SDL_GetTicks();
1279 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1280 steps_final = MIN(MAX(0, steps), max_steps);
1284 done = (steps_done >= steps_final);
1286 for (i = 0 ; i < melt_columns; i++)
1294 else if (ypos[i] < height)
1299 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1301 if (ypos[i] + dy >= height)
1302 dy = height - ypos[i];
1304 // copy part of (appearing) target surface to upper area
1305 src_rect.x = src_x + i * melt_pixels;
1306 // src_rect.y = src_y + ypos[i];
1308 src_rect.w = melt_pixels;
1310 src_rect.h = ypos[i] + dy;
1312 dst_rect.x = dst_x + i * melt_pixels;
1313 // dst_rect.y = dst_y + ypos[i];
1316 if (steps_done >= steps_final)
1317 SDL_BlitSurface(surface_target, &src_rect,
1318 surface_screen, &dst_rect);
1322 // copy part of (disappearing) source surface to lower area
1323 src_rect.x = src_x + i * melt_pixels;
1325 src_rect.w = melt_pixels;
1326 src_rect.h = height - ypos[i];
1328 dst_rect.x = dst_x + i * melt_pixels;
1329 dst_rect.y = dst_y + ypos[i];
1331 if (steps_done >= steps_final)
1332 SDL_BlitSurface(surface_source, &src_rect,
1333 surface_screen, &dst_rect);
1339 src_rect.x = src_x + i * melt_pixels;
1341 src_rect.w = melt_pixels;
1342 src_rect.h = height;
1344 dst_rect.x = dst_x + i * melt_pixels;
1347 if (steps_done >= steps_final)
1348 SDL_BlitSurface(surface_target, &src_rect,
1349 surface_screen, &dst_rect);
1353 if (steps_done >= steps_final)
1355 if (draw_border_function != NULL)
1356 draw_border_function();
1358 UpdateScreen_WithFrameDelay(&dst_rect2);
1360 if (PendingEscapeKeyEvent())
1365 else if (fade_mode == FADE_MODE_CURTAIN)
1369 int xx_size = width / 2;
1371 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1373 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1375 for (xx = 0; xx < xx_size;)
1377 time_last = time_current;
1378 time_current = SDL_GetTicks();
1379 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1380 xx_final = MIN(MAX(0, xx), xx_size);
1385 src_rect.h = height;
1390 // draw new (target) image to screen buffer
1391 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1393 if (xx_final < xx_size)
1395 src_rect.w = xx_size - xx_final;
1396 src_rect.h = height;
1398 // draw old (source) image to screen buffer (left side)
1400 src_rect.x = src_x + xx_final;
1403 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1405 // draw old (source) image to screen buffer (right side)
1407 src_rect.x = src_x + xx_size;
1408 dst_rect.x = dst_x + xx_size + xx_final;
1410 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1413 if (draw_border_function != NULL)
1414 draw_border_function();
1416 // only update the region of the screen that is affected from fading
1417 UpdateScreen_WithFrameDelay(&dst_rect2);
1419 if (PendingEscapeKeyEvent())
1423 else // fading in, fading out or cross-fading
1428 for (alpha = 0.0; alpha < 255.0;)
1430 time_last = time_current;
1431 time_current = SDL_GetTicks();
1432 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1433 alpha_final = MIN(MAX(0, alpha), 255);
1435 // draw existing (source) image to screen buffer
1436 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1438 // draw new (target) image to screen buffer using alpha blending
1439 SDLSetAlpha(surface_target, TRUE, alpha_final);
1440 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1442 if (draw_border_function != NULL)
1443 draw_border_function();
1445 // only update the region of the screen that is affected from fading
1446 UpdateScreen_WithFrameDelay(&dst_rect);
1448 if (PendingEscapeKeyEvent())
1454 Delay_WithScreenUpdates(post_delay);
1456 // restore function for drawing global masked border
1457 gfx.draw_global_border_function = draw_global_border_function;
1459 // after fading in, restore backbuffer (without animation graphics)
1460 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1461 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1464 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1465 int to_x, int to_y, Uint32 color)
1467 SDL_Surface *surface = dst_bitmap->surface;
1471 swap_numbers(&from_x, &to_x);
1474 swap_numbers(&from_y, &to_y);
1478 rect.w = (to_x - from_x + 1);
1479 rect.h = (to_y - from_y + 1);
1481 SDL_FillRect(surface, &rect, color);
1484 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1485 int to_x, int to_y, Uint32 color)
1487 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1490 #if ENABLE_UNUSED_CODE
1491 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1492 int num_points, Uint32 color)
1497 for (i = 0; i < num_points - 1; i++)
1499 for (x = 0; x < line_width; x++)
1501 for (y = 0; y < line_width; y++)
1503 int dx = x - line_width / 2;
1504 int dy = y - line_width / 2;
1506 if ((x == 0 && y == 0) ||
1507 (x == 0 && y == line_width - 1) ||
1508 (x == line_width - 1 && y == 0) ||
1509 (x == line_width - 1 && y == line_width - 1))
1512 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1513 points[i + 1].x + dx, points[i + 1].y + dy, color);
1520 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1522 SDL_Surface *surface = src_bitmap->surface;
1524 switch (surface->format->BytesPerPixel)
1526 case 1: // assuming 8-bpp
1528 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1532 case 2: // probably 15-bpp or 16-bpp
1534 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1538 case 3: // slow 24-bpp mode; usually not used
1541 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1545 shift = surface->format->Rshift;
1546 color |= *(pix + shift / 8) >> shift;
1547 shift = surface->format->Gshift;
1548 color |= *(pix + shift / 8) >> shift;
1549 shift = surface->format->Bshift;
1550 color |= *(pix + shift / 8) >> shift;
1556 case 4: // probably 32-bpp
1558 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1567 // ============================================================================
1568 // The following functions were taken from the SGE library
1569 // (SDL Graphics Extension Library) by Anders Lindström
1570 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1571 // ============================================================================
1573 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1575 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1577 switch (surface->format->BytesPerPixel)
1582 *((Uint8 *)surface->pixels + y * surface->pitch + x) = color;
1588 // Probably 15-bpp or 16-bpp
1589 *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x) = color;
1595 // Slow 24-bpp mode, usually not used
1599 // Gack - slow, but endian correct
1600 pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1601 shift = surface->format->Rshift;
1602 *(pix + shift / 8) = color>>shift;
1603 shift = surface->format->Gshift;
1604 *(pix + shift / 8) = color>>shift;
1605 shift = surface->format->Bshift;
1606 *(pix + shift / 8) = color>>shift;
1613 *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x) = color;
1621 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1622 Uint8 R, Uint8 G, Uint8 B)
1624 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1627 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1629 *((Uint8 *)surface->pixels + y * surface->pitch + x) = color;
1632 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1634 *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x) = color;
1637 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1642 // Gack - slow, but endian correct
1643 pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1644 shift = surface->format->Rshift;
1645 *(pix + shift / 8) = color>>shift;
1646 shift = surface->format->Gshift;
1647 *(pix + shift / 8) = color>>shift;
1648 shift = surface->format->Bshift;
1649 *(pix + shift / 8) = color>>shift;
1652 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1654 *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x) = color;
1657 static void _PutPixelX(SDL_Surface *dest, Sint16 x, Sint16 y, Uint32 color)
1659 switch (dest->format->BytesPerPixel)
1662 *((Uint8 *)dest->pixels + y * dest->pitch + x) = color;
1666 *((Uint16 *)dest->pixels + y * dest->pitch / 2 + x) = color;
1670 _PutPixel24(dest, x, y, color);
1674 *((Uint32 *)dest->pixels + y * dest->pitch / 4 + x) = color;
1680 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1682 if (SDL_MUSTLOCK(surface))
1684 if (SDL_LockSurface(surface) < 0)
1690 _PutPixel(surface, x, y, color);
1692 if (SDL_MUSTLOCK(surface))
1694 SDL_UnlockSurface(surface);
1699 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1700 Uint8 r, Uint8 g, Uint8 b)
1702 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1705 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1707 if (y >= 0 && y <= dest->h - 1)
1709 switch (dest->format->BytesPerPixel)
1712 return y * dest->pitch;
1716 return y * dest->pitch / 2;
1720 return y * dest->pitch;
1724 return y * dest->pitch / 4;
1732 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1735 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1737 switch (surface->format->BytesPerPixel)
1742 *((Uint8 *)surface->pixels + ypitch + x) = color;
1748 // Probably 15-bpp or 16-bpp
1749 *((Uint16 *)surface->pixels + ypitch + x) = color;
1755 // Slow 24-bpp mode, usually not used
1759 // Gack - slow, but endian correct
1760 pix = (Uint8 *)surface->pixels + ypitch + x * 3;
1761 shift = surface->format->Rshift;
1762 *(pix + shift / 8) = color>>shift;
1763 shift = surface->format->Gshift;
1764 *(pix + shift / 8) = color>>shift;
1765 shift = surface->format->Bshift;
1766 *(pix + shift / 8) = color>>shift;
1773 *((Uint32 *)surface->pixels + ypitch + x) = color;
1780 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1785 if (SDL_MUSTLOCK(Surface))
1787 if (SDL_LockSurface(Surface) < 0)
1801 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1805 if (x2 > Surface->w - 1)
1806 x2 = Surface->w - 1;
1813 SDL_FillRect(Surface, &l, Color);
1815 if (SDL_MUSTLOCK(Surface))
1817 SDL_UnlockSurface(Surface);
1821 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1822 Uint8 R, Uint8 G, Uint8 B)
1824 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1827 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1840 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1844 if (x2 > Surface->w - 1)
1845 x2 = Surface->w - 1;
1852 SDL_FillRect(Surface, &l, Color);
1855 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1860 if (SDL_MUSTLOCK(Surface))
1862 if (SDL_LockSurface(Surface) < 0)
1876 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1880 if (y2 > Surface->h - 1)
1881 y2 = Surface->h - 1;
1888 SDL_FillRect(Surface, &l, Color);
1890 if (SDL_MUSTLOCK(Surface))
1892 SDL_UnlockSurface(Surface);
1896 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1897 Uint8 R, Uint8 G, Uint8 B)
1899 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1902 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1915 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1919 if (y2 > Surface->h - 1)
1920 y2 = Surface->h - 1;
1927 SDL_FillRect(Surface, &l, Color);
1931 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1932 Sint16 x2, Sint16 y2, Uint32 Color,
1933 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1936 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1941 sdx = (dx < 0) ? -1 : 1;
1942 sdy = (dy < 0) ? -1 : 1;
1954 for (x = 0; x < dx; x++)
1956 Callback(Surface, px, py, Color);
1970 for (y = 0; y < dy; y++)
1972 Callback(Surface, px, py, Color);
1987 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1988 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1989 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1992 sge_DoLine(Surface, X1, Y1, X2, Y2,
1993 SDL_MapRGB(Surface->format, R, G, B), Callback);
1997 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
2000 if (SDL_MUSTLOCK(Surface))
2002 if (SDL_LockSurface(Surface) < 0)
2007 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
2009 // unlock the display
2010 if (SDL_MUSTLOCK(Surface))
2012 SDL_UnlockSurface(Surface);
2017 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
2018 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
2020 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
2024 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
2026 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
2030 // ----------------------------------------------------------------------------
2031 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
2032 // ----------------------------------------------------------------------------
2034 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
2035 int src_x, int src_y, int width, int height,
2036 int dst_x, int dst_y)
2040 for (y = 0; y < height; y++)
2042 for (x = 0; x < width; x++)
2044 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
2046 if (pixel != BLACK_PIXEL)
2047 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
2053 // ============================================================================
2054 // The following functions were taken from the SDL_gfx library version 2.0.3
2055 // (Rotozoomer) by Andreas Schiffler
2056 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
2057 // ============================================================================
2059 // ----------------------------------------------------------------------------
2062 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
2063 // ----------------------------------------------------------------------------
2073 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
2076 tColorRGBA *sp, *csp, *dp;
2080 sp = csp = (tColorRGBA *) src->pixels;
2081 dp = (tColorRGBA *) dst->pixels;
2082 dgap = dst->pitch - dst->w * 4;
2084 for (y = 0; y < dst->h; y++)
2088 for (x = 0; x < dst->w; x++)
2090 tColorRGBA *sp0 = sp;
2091 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
2092 tColorRGBA *sp00 = &sp0[0];
2093 tColorRGBA *sp01 = &sp0[1];
2094 tColorRGBA *sp10 = &sp1[0];
2095 tColorRGBA *sp11 = &sp1[1];
2098 // create new color pixel from all four source color pixels
2099 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
2100 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
2101 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
2102 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
2107 // advance source pointers
2110 // advance destination pointer
2114 // advance source pointer
2115 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2117 // advance destination pointers
2118 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2124 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2126 int x, y, *sax, *say, *csax, *csay;
2128 tColorRGBA *sp, *csp, *csp0, *dp;
2131 // use specialized zoom function when scaling down to exactly half size
2132 if (src->w == 2 * dst->w &&
2133 src->h == 2 * dst->h)
2134 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2137 sx = (float) src->w / (float) dst->w;
2138 sy = (float) src->h / (float) dst->h;
2140 // allocate memory for row increments
2141 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2142 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2144 // precalculate row increments
2145 for (x = 0; x <= dst->w; x++)
2146 *csax++ = (int)(sx * x);
2148 for (y = 0; y <= dst->h; y++)
2149 *csay++ = (int)(sy * y);
2152 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2153 dp = (tColorRGBA *) dst->pixels;
2154 dgap = dst->pitch - dst->w * 4;
2157 for (y = 0; y < dst->h; y++)
2162 for (x = 0; x < dst->w; x++)
2167 // advance source pointers
2171 // advance destination pointer
2175 // advance source pointer
2177 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2179 // advance destination pointers
2180 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2189 // ----------------------------------------------------------------------------
2192 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2193 // ----------------------------------------------------------------------------
2195 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2197 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2198 Uint8 *sp, *dp, *csp;
2202 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2203 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2205 // allocate memory for row increments
2206 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2207 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2209 // precalculate row increments
2212 for (x = 0; x < dst->w; x++)
2215 *csax = (csx >> 16);
2222 for (y = 0; y < dst->h; y++)
2225 *csay = (csy >> 16);
2232 for (x = 0; x < dst->w; x++)
2240 for (y = 0; y < dst->h; y++)
2247 sp = csp = (Uint8 *) src->pixels;
2248 dp = (Uint8 *) dst->pixels;
2249 dgap = dst->pitch - dst->w;
2253 for (y = 0; y < dst->h; y++)
2257 for (x = 0; x < dst->w; x++)
2262 // advance source pointers
2266 // advance destination pointer
2270 // advance source pointer (for row)
2271 csp += ((*csay) * src->pitch);
2274 // advance destination pointers
2284 // ----------------------------------------------------------------------------
2287 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2288 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2289 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2290 // into a 32bit RGBA format on the fly.
2291 // ----------------------------------------------------------------------------
2293 SDL_Surface *SDLZoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2295 SDL_Surface *zoom_src = NULL;
2296 SDL_Surface *zoom_dst = NULL;
2297 boolean is_converted = FALSE;
2304 // determine if source surface is 32 bit or 8 bit
2305 is_32bit = (src->format->BitsPerPixel == 32);
2307 if (is_32bit || src->format->BitsPerPixel == 8)
2309 // use source surface 'as is'
2314 // new source surface is 32 bit with a defined RGB ordering
2315 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2316 0x000000ff, 0x0000ff00, 0x00ff0000,
2317 (src->format->Amask ? 0xff000000 : 0));
2318 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2320 is_converted = TRUE;
2323 // allocate surface to completely contain the zoomed surface
2326 // target surface is 32 bit with source RGBA/ABGR ordering
2327 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2328 zoom_src->format->Rmask,
2329 zoom_src->format->Gmask,
2330 zoom_src->format->Bmask,
2331 zoom_src->format->Amask);
2335 // target surface is 8 bit
2336 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2340 // lock source surface
2341 SDL_LockSurface(zoom_src);
2343 // check which kind of surface we have
2346 // call the 32 bit transformation routine to do the zooming
2347 zoomSurfaceRGBA(zoom_src, zoom_dst);
2352 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2353 zoom_dst->format->palette->colors[i] =
2354 zoom_src->format->palette->colors[i];
2355 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2357 // call the 8 bit transformation routine to do the zooming
2358 zoomSurfaceY(zoom_src, zoom_dst);
2361 // unlock source surface
2362 SDL_UnlockSurface(zoom_src);
2364 // free temporary surface
2366 SDL_FreeSurface(zoom_src);
2368 // return destination surface
2372 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2374 Bitmap *dst_bitmap = CreateBitmapStruct();
2375 SDL_Surface *src_surface = src_bitmap->surface_masked;
2376 SDL_Surface *dst_surface;
2378 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2379 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2381 dst_bitmap->width = dst_width;
2382 dst_bitmap->height = dst_height;
2384 // create zoomed temporary surface from source surface
2385 dst_surface = SDLZoomSurface(src_surface, dst_width, dst_height);
2387 // create native format destination surface from zoomed temporary surface
2388 SDLSetNativeSurface(&dst_surface);
2390 // set color key for zoomed surface from source surface, if defined
2391 if (SDLHasColorKey(src_surface))
2392 SDLCopyColorKey(src_surface, dst_surface);
2394 // create native non-transparent surface for opaque blitting
2395 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2397 // set native transparent surface for masked blitting
2398 dst_bitmap->surface_masked = dst_surface;
2404 // ============================================================================
2405 // load image to bitmap
2406 // ============================================================================
2408 Bitmap *SDLLoadImage(char *filename)
2410 Bitmap *new_bitmap = CreateBitmapStruct();
2411 SDL_Surface *sdl_image_tmp;
2413 if (program.headless)
2415 // prevent sanity check warnings at later stage
2416 new_bitmap->width = new_bitmap->height = 1;
2421 print_timestamp_init("SDLLoadImage");
2423 print_timestamp_time(getBaseNamePtr(filename));
2425 // load image to temporary surface
2426 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2427 Fail("IMG_Load('%s') failed: %s", getBaseNamePtr(filename), SDL_GetError());
2429 print_timestamp_time("IMG_Load");
2431 UPDATE_BUSY_STATE();
2433 // create native non-transparent surface for current image
2434 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2435 Fail("SDLGetOpaqueSurface() failed");
2437 print_timestamp_time("SDLGetNativeSurface (opaque)");
2439 UPDATE_BUSY_STATE();
2441 // set black pixel to transparent if no alpha channel / transparent color
2442 if (!SDLHasAlpha(sdl_image_tmp) &&
2443 !SDLHasColorKey(sdl_image_tmp))
2444 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2445 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2447 // create native transparent surface for current image
2448 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2449 Fail("SDLGetNativeSurface() failed");
2451 print_timestamp_time("SDLGetNativeSurface (masked)");
2453 UPDATE_BUSY_STATE();
2455 // free temporary surface
2456 SDL_FreeSurface(sdl_image_tmp);
2458 new_bitmap->width = new_bitmap->surface->w;
2459 new_bitmap->height = new_bitmap->surface->h;
2461 print_timestamp_done("SDLLoadImage");
2467 // ----------------------------------------------------------------------------
2468 // custom cursor fuctions
2469 // ----------------------------------------------------------------------------
2471 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2473 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2474 cursor_info->width, cursor_info->height,
2475 cursor_info->hot_x, cursor_info->hot_y);
2478 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2480 static struct MouseCursorInfo *last_cursor_info = NULL;
2481 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2482 static SDL_Cursor *cursor_default = NULL;
2483 static SDL_Cursor *cursor_current = NULL;
2485 // if invoked for the first time, store the SDL default cursor
2486 if (cursor_default == NULL)
2487 cursor_default = SDL_GetCursor();
2489 // only create new cursor if cursor info (custom only) has changed
2490 if (cursor_info != NULL && cursor_info != last_cursor_info)
2492 cursor_current = create_cursor(cursor_info);
2493 last_cursor_info = cursor_info;
2496 // only set new cursor if cursor info (custom or NULL) has changed
2497 if (cursor_info != last_cursor_info2)
2498 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2500 last_cursor_info2 = cursor_info;
2504 // ============================================================================
2506 // ============================================================================
2508 void SDLOpenAudio(void)
2510 if (program.headless)
2513 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2515 Warn("SDL_InitSubSystem() failed: %s", SDL_GetError());
2520 // set audio sample rate for mixer
2521 audio.sample_rate = (setup.audio_sample_rate_44100 ? 44100 : 22050);
2523 if (Mix_OpenAudio(audio.sample_rate, MIX_DEFAULT_FORMAT, AUDIO_NUM_CHANNELS_STEREO,
2524 setup.system.audio_fragment_size) < 0)
2526 Warn("Mix_OpenAudio() failed: %s", SDL_GetError());
2531 audio.sound_available = TRUE;
2532 audio.music_available = TRUE;
2533 audio.loops_available = TRUE;
2534 audio.sound_enabled = TRUE;
2536 // set number of available mixer channels
2537 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2538 audio.music_channel = MUSIC_CHANNEL;
2539 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2541 Mixer_InitChannels();
2544 void SDLCloseAudio(void)
2547 Mix_HaltChannel(-1);
2550 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2553 void SDLReopenAudio(void)
2560 // ============================================================================
2562 // ============================================================================
2564 void SDLWaitEvent(Event *event)
2566 SDL_WaitEvent(event);
2569 void SDLCorrectRawMousePosition(int *x, int *y)
2571 if (sdl_renderer == NULL)
2574 // this corrects the raw mouse position for logical screen size within event
2575 // filters (correction done later by SDL library when handling mouse events)
2578 float scale_x, scale_y;
2580 SDL_RenderGetViewport(sdl_renderer, &viewport);
2581 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2583 *x = (int)(*x / scale_x);
2584 *y = (int)(*y / scale_y);
2591 // ============================================================================
2592 // joystick functions
2593 // ============================================================================
2595 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2596 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2597 static int sdl_js_axis[MAX_PLAYERS][2];
2598 static int sdl_js_button[MAX_PLAYERS][2];
2599 static boolean sdl_is_controller[MAX_PLAYERS];
2601 void SDLClearJoystickState(void)
2605 for (i = 0; i < MAX_PLAYERS; i++)
2607 for (j = 0; j < 2; j++)
2609 sdl_js_axis_raw[i][j] = -1;
2610 sdl_js_axis[i][j] = 0;
2611 sdl_js_button[i][j] = 0;
2616 boolean SDLOpenJoystick(int nr)
2618 if (nr < 0 || nr >= MAX_PLAYERS)
2621 sdl_is_controller[nr] = SDL_IsGameController(nr);
2624 Debug("joystick", "opening joystick %d (%s)",
2625 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2628 if (sdl_is_controller[nr])
2629 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2631 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2633 return (sdl_joystick[nr] != NULL);
2636 void SDLCloseJoystick(int nr)
2638 if (nr < 0 || nr >= MAX_PLAYERS)
2642 Debug("joystick", "closing joystick %d", nr);
2645 if (sdl_is_controller[nr])
2646 SDL_GameControllerClose(sdl_joystick[nr]);
2648 SDL_JoystickClose(sdl_joystick[nr]);
2650 sdl_joystick[nr] = NULL;
2653 boolean SDLCheckJoystickOpened(int nr)
2655 if (nr < 0 || nr >= MAX_PLAYERS)
2658 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2661 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2663 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2664 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2665 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2666 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2668 if (nr < 0 || nr >= MAX_PLAYERS)
2674 // prevent (slightly jittering, but centered) axis A from resetting axis B
2675 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2676 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2679 sdl_js_axis[nr][axis_id] = axis_value;
2680 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2683 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2685 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2686 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2687 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2688 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2689 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2690 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2691 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2692 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2695 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2696 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2697 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2698 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2699 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2700 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2701 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2702 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2704 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2705 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2706 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2707 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2708 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2710 if (nr < 0 || nr >= MAX_PLAYERS)
2713 if (button_id == -1)
2716 sdl_js_button[nr][button_id] = button_state;
2719 void HandleJoystickEvent(Event *event)
2721 // when using joystick, disable overlay touch buttons
2722 runtime.uses_touch_device = FALSE;
2724 switch (event->type)
2726 case SDL_CONTROLLERDEVICEADDED:
2728 Debug("joystick", "SDL_CONTROLLERDEVICEADDED: device %d added",
2729 event->cdevice.which);
2734 case SDL_CONTROLLERDEVICEREMOVED:
2736 Debug("joystick", "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2737 event->cdevice.which);
2742 case SDL_CONTROLLERAXISMOTION:
2744 Debug("joystick", "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2745 event->caxis.which, event->caxis.axis, event->caxis.value);
2747 setJoystickAxis(event->caxis.which,
2749 event->caxis.value);
2752 case SDL_CONTROLLERBUTTONDOWN:
2754 Debug("joystick", "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2755 event->cbutton.which, event->cbutton.button);
2757 setJoystickButton(event->cbutton.which,
2758 event->cbutton.button,
2762 case SDL_CONTROLLERBUTTONUP:
2764 Debug("joystick", "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2765 event->cbutton.which, event->cbutton.button);
2767 setJoystickButton(event->cbutton.which,
2768 event->cbutton.button,
2772 case SDL_JOYAXISMOTION:
2773 if (sdl_is_controller[event->jaxis.which])
2777 Debug("joystick", "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2778 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2780 if (event->jaxis.axis < 4)
2781 setJoystickAxis(event->jaxis.which,
2783 event->jaxis.value);
2786 case SDL_JOYBUTTONDOWN:
2787 if (sdl_is_controller[event->jaxis.which])
2791 Debug("joystick", "SDL_JOYBUTTONDOWN: device %d, button %d",
2792 event->jbutton.which, event->jbutton.button);
2794 if (event->jbutton.button < 4)
2795 setJoystickButton(event->jbutton.which,
2796 event->jbutton.button,
2800 case SDL_JOYBUTTONUP:
2801 if (sdl_is_controller[event->jaxis.which])
2805 Debug("joystick", "SDL_JOYBUTTONUP: device %d, button %d",
2806 event->jbutton.which, event->jbutton.button);
2808 if (event->jbutton.button < 4)
2809 setJoystickButton(event->jbutton.which,
2810 event->jbutton.button,
2819 void SDLInitJoysticks(void)
2821 static boolean sdl_joystick_subsystem_initialized = FALSE;
2822 boolean print_warning = !sdl_joystick_subsystem_initialized;
2823 char *mappings_file_base = getPath2(options.conf_directory,
2824 GAMECONTROLLER_BASENAME);
2825 char *mappings_file_user = getPath2(getMainUserGameDataDir(),
2826 GAMECONTROLLER_BASENAME);
2830 if (!sdl_joystick_subsystem_initialized)
2832 sdl_joystick_subsystem_initialized = TRUE;
2834 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2836 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2837 Fail("SDL_Init() failed: %s", SDL_GetError());
2839 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2841 // the included game controller base mappings should always be found
2842 if (num_mappings == -1)
2843 Warn("no game controller base mappings found");
2846 Debug("joystick", "%d game controller base mapping(s) added",
2850 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2853 // the personal game controller user mappings may or may not be found
2854 if (num_mappings == -1)
2855 Warn("no game controller user mappings found");
2857 Debug("joystick", , "%d game controller user mapping(s) added",
2860 Debug("joystick", "%d joystick(s) found:", SDL_NumJoysticks());
2863 checked_free(mappings_file_base);
2864 checked_free(mappings_file_user);
2867 for (i = 0; i < SDL_NumJoysticks(); i++)
2869 const char *name, *type;
2871 if (SDL_IsGameController(i))
2873 name = SDL_GameControllerNameForIndex(i);
2874 type = "game controller";
2878 name = SDL_JoystickNameForIndex(i);
2882 Debug("joystick", "- joystick %d (%s): '%s'",
2883 i, type, (name ? name : "(Unknown)"));
2888 // assign joysticks from configured to connected joystick for all players
2889 for (i = 0; i < MAX_PLAYERS; i++)
2891 // get configured joystick for this player
2892 char *device_name = setup.input[i].joy.device_name;
2893 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2895 if (joystick_nr >= SDL_NumJoysticks())
2897 if (setup.input[i].use_joystick && print_warning)
2898 Warn("cannot find joystick %d", joystick_nr);
2903 // store configured joystick number for each player
2904 joystick.nr[i] = joystick_nr;
2907 // now open all connected joysticks (regardless if configured or not)
2908 for (i = 0; i < SDL_NumJoysticks(); i++)
2910 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2911 if (SDLCheckJoystickOpened(i))
2912 SDLCloseJoystick(i);
2914 if (SDLOpenJoystick(i))
2915 joystick.status = JOYSTICK_ACTIVATED;
2916 else if (print_warning)
2917 Warn("cannot open joystick %d", i);
2920 SDLClearJoystickState();
2923 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2925 if (nr < 0 || nr >= MAX_PLAYERS)
2929 *x = sdl_js_axis[nr][0];
2931 *y = sdl_js_axis[nr][1];
2934 *b1 = sdl_js_button[nr][0];
2936 *b2 = sdl_js_button[nr][1];
2942 // ============================================================================
2943 // touch input overlay functions
2944 // ============================================================================
2946 #if defined(USE_TOUCH_INPUT_OVERLAY)
2947 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2950 int grid_xsize = overlay.grid_xsize;
2951 int grid_ysize = overlay.grid_ysize;
2954 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2955 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2957 for (x = 0; x < grid_xsize; x++)
2959 rect.x = (x + 0) * video.screen_width / grid_xsize;
2960 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2962 for (y = 0; y < grid_ysize; y++)
2964 rect.y = (y + 0) * video.screen_height / grid_ysize;
2965 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2967 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2968 SDL_RenderDrawRect(sdl_renderer, &rect);
2972 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2975 static void RenderFillRectangle(int x, int y, int width, int height)
2977 SDL_Rect rect = { x, y, width, height };
2979 SDL_RenderFillRect(sdl_renderer, &rect);
2982 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2984 static int alpha_direction = 0;
2985 static int alpha_highlight = 0;
2986 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2987 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2989 int grid_xsize = overlay.grid_xsize;
2990 int grid_ysize = overlay.grid_ysize;
2993 if (alpha == alpha_max)
2995 if (alpha_direction < 0)
2997 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2999 if (alpha_highlight == 0)
3000 alpha_direction = 1;
3004 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
3006 if (alpha_highlight == alpha_max)
3007 alpha_direction = -1;
3012 alpha_direction = 1;
3013 alpha_highlight = alpha;
3016 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
3018 for (x = 0; x < grid_xsize; x++)
3020 for (y = 0; y < grid_ysize; y++)
3022 int grid_button = overlay.grid_button[x][y];
3023 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
3024 int alpha_draw = alpha;
3025 int outline_border = MV_NONE;
3026 int border_size = 2;
3027 boolean draw_outlined = setup.touch.draw_outlined;
3028 boolean draw_pressed = setup.touch.draw_pressed;
3030 if (grid_button == CHAR_GRID_BUTTON_NONE)
3033 if (grid_button == overlay.grid_button_highlight)
3035 draw_outlined = FALSE;
3036 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
3039 if (draw_pressed && overlay.grid_button_action & grid_button_action)
3042 draw_outlined = FALSE;
3044 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
3047 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
3049 rect.x = (x + 0) * video.screen_width / grid_xsize;
3050 rect.y = (y + 0) * video.screen_height / grid_ysize;
3051 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
3052 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
3054 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
3056 rect.x += border_size;
3057 rect.w -= border_size;
3059 outline_border |= MV_LEFT;
3062 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
3064 rect.w -= border_size;
3066 outline_border |= MV_RIGHT;
3069 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
3071 rect.y += border_size;
3072 rect.h -= border_size;
3074 outline_border |= MV_UP;
3077 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
3079 rect.h -= border_size;
3081 outline_border |= MV_DOWN;
3086 int rect_x = rect.x +
3087 (outline_border & MV_LEFT ? border_size : 0);
3088 int rect_w = rect.w -
3089 (outline_border & MV_LEFT ? border_size : 0) -
3090 (outline_border & MV_RIGHT ? border_size : 0);
3092 if (outline_border & MV_LEFT)
3093 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
3095 if (outline_border & MV_RIGHT)
3096 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
3097 border_size, rect.h);
3099 if (outline_border & MV_UP)
3100 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
3102 if (outline_border & MV_DOWN)
3103 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
3104 rect_w, border_size);
3108 SDL_RenderFillRect(sdl_renderer, &rect);
3113 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3116 static void DrawTouchInputOverlay(void)
3118 static boolean deactivated = TRUE;
3119 static boolean show_grid = FALSE;
3120 static int alpha = 0;
3121 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3122 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3123 boolean active = (overlay.enabled && overlay.active);
3125 if (!active && deactivated)
3130 if (alpha < alpha_max)
3131 alpha = MIN(alpha + alpha_step, alpha_max);
3133 deactivated = FALSE;
3137 alpha = MAX(0, alpha - alpha_step);
3143 if (overlay.show_grid)
3145 else if (deactivated)
3149 DrawTouchInputOverlay_ShowGrid(alpha);
3151 DrawTouchInputOverlay_ShowGridButtons(alpha);
3154 static void DrawTouchGadgetsOverlay(void)
3156 DrawGadgets_OverlayTouchButtons();