1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
19 #define ENABLE_UNUSED_CODE 0 // currently unused functions
21 #define DEBUG_JOYSTICKS 0
24 // ============================================================================
26 // ============================================================================
28 // SDL internal variables
29 static SDL_Window *sdl_window = NULL;
30 static SDL_Renderer *sdl_renderer = NULL;
31 static SDL_Texture *sdl_texture_stream = NULL;
32 static SDL_Texture *sdl_texture_target = NULL;
33 static boolean fullscreen_enabled = FALSE;
34 static boolean limit_screen_updates = FALSE;
37 // functions from SGE library
38 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
40 #if defined(USE_TOUCH_INPUT_OVERLAY)
41 // functions to draw overlay graphics for touch device input
42 static void DrawTouchInputOverlay(void);
43 static void DrawTouchGadgetsOverlay(void);
46 void SDLLimitScreenUpdates(boolean enable)
48 limit_screen_updates = enable;
51 static void FinalizeScreen(int draw_target)
53 // copy global animations to render target buffer, if defined (below border)
54 if (gfx.draw_global_anim_function != NULL)
55 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
57 // copy global masked border to render target buffer, if defined
58 if (gfx.draw_global_border_function != NULL)
59 gfx.draw_global_border_function(draw_target);
61 // copy global animations to render target buffer, if defined (above border)
62 if (gfx.draw_global_anim_function != NULL)
63 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
65 // copy tile selection cursor to render target buffer, if defined (above all)
66 if (gfx.draw_tile_cursor_function != NULL)
67 gfx.draw_tile_cursor_function(draw_target);
70 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
72 static unsigned int update_screen_delay = 0;
73 unsigned int update_screen_delay_value = 50; // (milliseconds)
74 SDL_Surface *screen = backbuffer->surface;
76 if (limit_screen_updates &&
77 !DelayReached(&update_screen_delay, update_screen_delay_value))
80 LimitScreenUpdates(FALSE);
84 static int LastFrameCounter = 0;
85 boolean changed = (FrameCounter != LastFrameCounter);
87 printf("::: FrameCounter == %d [%s]\n", FrameCounter,
88 (changed ? "-" : "SAME FRAME UPDATED"));
90 LastFrameCounter = FrameCounter;
99 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
100 gfx.final_screen_bitmap != NULL) // may not be initialized yet
102 // draw global animations using bitmaps instead of using textures
103 // to prevent texture scaling artefacts (this is potentially slower)
105 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
106 gfx.win_xsize, gfx.win_ysize, 0, 0);
108 FinalizeScreen(DRAW_TO_SCREEN);
110 screen = gfx.final_screen_bitmap->surface;
112 // force full window redraw
116 SDL_Texture *sdl_texture = sdl_texture_stream;
118 // deactivate use of target texture if render targets are not supported
119 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
120 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
121 sdl_texture_target == NULL)
122 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
124 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
125 sdl_texture = sdl_texture_target;
129 int bytes_x = screen->pitch / video.width;
130 int bytes_y = screen->pitch;
132 SDL_UpdateTexture(sdl_texture, rect,
133 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
138 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
141 int xoff = video.screen_xoffset;
142 int yoff = video.screen_yoffset;
143 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
144 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
145 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
147 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
148 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
149 dst_rect2 = &dst_rect_screen;
151 dst_rect1 = &dst_rect_screen;
153 #if defined(HAS_SCREEN_KEYBOARD)
154 if (video.shifted_up || video.shifted_up_delay)
156 int time_current = SDL_GetTicks();
157 int pos = video.shifted_up_pos;
158 int pos_last = video.shifted_up_pos_last;
160 if (!DelayReachedExt(&video.shifted_up_delay, video.shifted_up_delay_value,
163 int delay = time_current - video.shifted_up_delay;
164 int delay_value = video.shifted_up_delay_value;
166 pos = pos_last + (pos - pos_last) * delay / delay_value;
170 video.shifted_up_pos_last = pos;
171 video.shifted_up_delay = 0;
174 SDL_Rect src_rect_up = { 0, pos, video.width, video.height - pos };
175 SDL_Rect dst_rect_up = { xoff, yoff, video.width, video.height - pos };
177 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
178 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
180 src_rect2 = &src_rect_up;
181 dst_rect2 = &dst_rect_up;
185 src_rect1 = &src_rect_up;
186 dst_rect1 = &dst_rect_up;
191 // clear render target buffer
192 SDL_RenderClear(sdl_renderer);
194 // set renderer to use target texture for rendering
195 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
196 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
197 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
199 // copy backbuffer texture to render target buffer
200 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
201 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
203 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
204 FinalizeScreen(DRAW_TO_SCREEN);
206 // when using target texture, copy it to screen buffer
207 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
208 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
210 SDL_SetRenderTarget(sdl_renderer, NULL);
211 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
214 #if defined(USE_TOUCH_INPUT_OVERLAY)
215 // draw overlay graphics for touch device input, if needed
216 DrawTouchInputOverlay();
218 // draw overlay gadgets for touch device input, if needed
219 DrawTouchGadgetsOverlay();
222 // global synchronization point of the game to align video frame delay
223 if (with_frame_delay)
224 WaitUntilDelayReached(&video.frame_delay, video.frame_delay_value);
226 video.frame_counter++;
228 // show render target buffer on screen
229 SDL_RenderPresent(sdl_renderer);
232 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
234 PumpEvents(); // execute event filter actions while waiting
236 UpdateScreenExt(rect, TRUE);
239 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
241 UpdateScreenExt(rect, FALSE);
244 void Delay_WithScreenUpdates(unsigned int delay)
246 unsigned int time_current = SDL_GetTicks();
247 unsigned int time_delayed = time_current + delay;
249 while (time_current < time_delayed)
251 // updating the screen contains waiting for frame delay (non-busy)
252 UpdateScreen_WithFrameDelay(NULL);
254 time_current = SDL_GetTicks();
258 static void SDLSetWindowIcon(char *basename)
260 // (setting the window icon on Mac OS X would replace the high-quality
261 // dock icon with the currently smaller (and uglier) icon from file)
263 #if !defined(PLATFORM_MACOSX)
264 char *filename = getCustomImageFilename(basename);
265 SDL_Surface *surface;
267 if (filename == NULL)
269 Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
274 if ((surface = IMG_Load(filename)) == NULL)
276 Error(ERR_WARN, "IMG_Load('%s') failed: %s", basename, SDL_GetError());
281 // set transparent color
282 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
283 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
285 SDL_SetWindowIcon(sdl_window, surface);
289 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
290 SDL_PixelFormat *format2)
292 return (format1->format == format2->format &&
293 format1->BitsPerPixel == format2->BitsPerPixel &&
294 format1->BytesPerPixel == format2->BytesPerPixel &&
295 format1->Rmask == format2->Rmask &&
296 format1->Gmask == format2->Gmask &&
297 format1->Bmask == format2->Bmask);
300 static Pixel SDLGetColorKey(SDL_Surface *surface)
304 if (SDL_GetColorKey(surface, &color_key) != 0)
310 static boolean SDLHasColorKey(SDL_Surface *surface)
312 return (SDLGetColorKey(surface) != -1);
315 static boolean SDLHasAlpha(SDL_Surface *surface)
317 SDL_BlendMode blend_mode;
319 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
322 return (blend_mode == SDL_BLENDMODE_BLEND);
325 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
327 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
329 SDL_SetSurfaceBlendMode(surface, blend_mode);
330 SDL_SetSurfaceAlphaMod(surface, alpha);
333 const char *SDLGetRendererName(void)
335 static SDL_RendererInfo renderer_info;
337 SDL_GetRendererInfo(sdl_renderer, &renderer_info);
339 return renderer_info.name;
342 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
344 SDL_PixelFormat format;
345 SDL_Surface *new_surface;
350 if (backbuffer && backbuffer->surface)
352 format = *backbuffer->surface->format;
353 format.Amask = surface->format->Amask; // keep alpha channel
357 format = *surface->format;
360 new_surface = SDL_ConvertSurface(surface, &format, 0);
362 if (new_surface == NULL)
363 Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
365 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
366 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
367 SDL_SetColorKey(new_surface, SET_TRANSPARENT_PIXEL,
368 SDLGetColorKey(surface));
373 boolean SDLSetNativeSurface(SDL_Surface **surface)
375 SDL_Surface *new_surface;
377 if (surface == NULL ||
379 backbuffer == NULL ||
380 backbuffer->surface == NULL)
383 // if pixel format already optimized for destination surface, do nothing
384 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
387 new_surface = SDLGetNativeSurface(*surface);
389 SDL_FreeSurface(*surface);
391 *surface = new_surface;
396 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
398 if (program.headless)
401 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
404 Error(ERR_EXIT, "SDL_CreateTextureFromSurface() failed: %s",
410 void SDLCreateBitmapTextures(Bitmap *bitmap)
416 SDL_DestroyTexture(bitmap->texture);
417 if (bitmap->texture_masked)
418 SDL_DestroyTexture(bitmap->texture_masked);
420 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
421 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
424 void SDLFreeBitmapTextures(Bitmap *bitmap)
430 SDL_DestroyTexture(bitmap->texture);
431 if (bitmap->texture_masked)
432 SDL_DestroyTexture(bitmap->texture_masked);
434 bitmap->texture = NULL;
435 bitmap->texture_masked = NULL;
438 void SDLInitVideoDisplay(void)
440 // set hint to select render driver as specified in setup config file
441 if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
442 SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
444 // initialize SDL video
445 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
446 Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
448 // set default SDL depth
449 video.default_depth = 32; // (how to determine video depth in SDL2?)
451 // Code used with SDL 1.2:
452 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
455 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
457 if (program.headless)
460 video.window_scaling_percent = setup.window_scaling_percent;
461 video.window_scaling_quality = setup.window_scaling_quality;
463 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
465 // SDL 2.0: support for (desktop) fullscreen mode available
466 video.fullscreen_available = TRUE;
468 // open SDL video output device (window or fullscreen mode)
469 if (!SDLSetVideoMode(fullscreen))
470 Error(ERR_EXIT, "setting video mode failed");
472 // !!! SDL2 can only set the window icon if the window already exists !!!
474 SDLSetWindowIcon(program.icon_filename);
476 // set window and icon title
480 static void SDLInitVideoBuffer_DrawBuffer(void)
482 /* SDL cannot directly draw to the visible video framebuffer like X11,
483 but always uses a backbuffer, which is then blitted to the visible
484 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
485 visible video framebuffer with 'SDL_Flip', if the hardware supports
486 this). Therefore do not use an additional backbuffer for drawing, but
487 use a symbolic buffer (distinguishable from the SDL backbuffer) called
488 'window', which indicates that the SDL backbuffer should be updated to
489 the visible video framebuffer when attempting to blit to it.
491 For convenience, it seems to be a good idea to create this symbolic
492 buffer 'window' at the same size as the SDL backbuffer. Although it
493 should never be drawn to directly, it would do no harm nevertheless. */
495 // create additional (symbolic) buffer for double-buffering
496 ReCreateBitmap(&window, video.width, video.height);
498 // create dummy drawing buffer for headless mode, if needed
499 if (program.headless)
500 ReCreateBitmap(&backbuffer, video.width, video.height);
503 void SDLInitVideoBuffer(boolean fullscreen)
505 SDLInitVideoBuffer_VideoBuffer(fullscreen);
506 SDLInitVideoBuffer_DrawBuffer();
509 static boolean SDLCreateScreen(boolean fullscreen)
511 SDL_Surface *new_surface = NULL;
513 int surface_flags_window = SURFACE_FLAGS;
514 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
517 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
519 video.vsync_mode = VSYNC_MODE_OFF;
521 if (!strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF))
523 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
524 video.vsync_mode = VSYNC_MODE_NORMAL;
527 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
528 _without_ enabling 2D/3D acceleration and/or guest additions installed,
529 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
530 it will try to use accelerated graphics and apparently fails miserably) */
531 int renderer_flags = SDL_RENDERER_SOFTWARE;
534 int width = video.width;
535 int height = video.height;
536 int screen_width = video.screen_width;
537 int screen_height = video.screen_height;
538 int surface_flags = (fullscreen ? surface_flags_fullscreen :
539 surface_flags_window);
541 // default window size is unscaled
542 video.window_width = screen_width;
543 video.window_height = screen_height;
545 // store if initial screen mode is fullscreen mode when changing screen size
546 video.fullscreen_initial = fullscreen;
548 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
550 video.window_width = window_scaling_factor * screen_width;
551 video.window_height = window_scaling_factor * screen_height;
553 if (sdl_texture_stream)
555 SDL_DestroyTexture(sdl_texture_stream);
556 sdl_texture_stream = NULL;
559 if (sdl_texture_target)
561 SDL_DestroyTexture(sdl_texture_target);
562 sdl_texture_target = NULL;
565 if (!(fullscreen && fullscreen_enabled))
569 SDL_DestroyRenderer(sdl_renderer);
575 SDL_DestroyWindow(sdl_window);
580 if (sdl_window == NULL)
581 sdl_window = SDL_CreateWindow(program.window_title,
582 SDL_WINDOWPOS_CENTERED,
583 SDL_WINDOWPOS_CENTERED,
588 if (sdl_window != NULL)
590 if (sdl_renderer == NULL)
591 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
593 if (sdl_renderer != NULL)
595 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
596 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
598 // required for setting adaptive vsync when using OpenGL renderer
599 SDLSetScreenVsyncMode(setup.vsync_mode);
601 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
602 SDL_PIXELFORMAT_ARGB8888,
603 SDL_TEXTUREACCESS_STREAMING,
606 if (SDL_RenderTargetSupported(sdl_renderer))
607 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
608 SDL_PIXELFORMAT_ARGB8888,
609 SDL_TEXTUREACCESS_TARGET,
612 if (sdl_texture_stream != NULL)
614 // use SDL default values for RGB masks and no alpha channel
615 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
617 if (new_surface == NULL)
618 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
622 Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
627 Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
632 Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
635 SDLSetScreenProperties();
637 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
638 if (new_surface != NULL)
639 fullscreen_enabled = fullscreen;
641 if (backbuffer == NULL)
642 backbuffer = CreateBitmapStruct();
644 backbuffer->width = video.width;
645 backbuffer->height = video.height;
647 if (backbuffer->surface)
648 SDL_FreeSurface(backbuffer->surface);
650 backbuffer->surface = new_surface;
652 return (new_surface != NULL);
655 boolean SDLSetVideoMode(boolean fullscreen)
657 boolean success = FALSE;
661 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
663 // switch display to fullscreen mode, if available
664 success = SDLCreateScreen(TRUE);
668 // switching display to fullscreen mode failed -- do not try it again
669 video.fullscreen_available = FALSE;
673 video.fullscreen_enabled = TRUE;
677 if ((!fullscreen && video.fullscreen_enabled) || !success)
679 // switch display to window mode
680 success = SDLCreateScreen(FALSE);
684 // switching display to window mode failed -- should not happen
688 video.fullscreen_enabled = FALSE;
689 video.window_scaling_percent = setup.window_scaling_percent;
690 video.window_scaling_quality = setup.window_scaling_quality;
692 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
696 SDLRedrawWindow(); // map window
701 void SDLSetWindowTitle(void)
703 if (sdl_window == NULL)
706 SDL_SetWindowTitle(sdl_window, program.window_title);
709 void SDLSetWindowScaling(int window_scaling_percent)
711 if (sdl_window == NULL)
714 float window_scaling_factor = (float)window_scaling_percent / 100;
715 int new_window_width = (int)(window_scaling_factor * video.screen_width);
716 int new_window_height = (int)(window_scaling_factor * video.screen_height);
718 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
720 video.window_scaling_percent = window_scaling_percent;
721 video.window_width = new_window_width;
722 video.window_height = new_window_height;
727 void SDLSetWindowScalingQuality(char *window_scaling_quality)
729 SDL_Texture *new_texture;
731 if (sdl_texture_stream == NULL)
734 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
736 new_texture = SDL_CreateTexture(sdl_renderer,
737 SDL_PIXELFORMAT_ARGB8888,
738 SDL_TEXTUREACCESS_STREAMING,
739 video.width, video.height);
741 if (new_texture != NULL)
743 SDL_DestroyTexture(sdl_texture_stream);
745 sdl_texture_stream = new_texture;
748 if (SDL_RenderTargetSupported(sdl_renderer))
749 new_texture = SDL_CreateTexture(sdl_renderer,
750 SDL_PIXELFORMAT_ARGB8888,
751 SDL_TEXTUREACCESS_TARGET,
752 video.width, video.height);
756 if (new_texture != NULL)
758 SDL_DestroyTexture(sdl_texture_target);
760 sdl_texture_target = new_texture;
765 video.window_scaling_quality = window_scaling_quality;
768 void SDLSetWindowFullscreen(boolean fullscreen)
770 if (sdl_window == NULL)
773 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
775 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
776 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
778 // if screen size was changed in fullscreen mode, correct desktop window size
779 if (!fullscreen && video.fullscreen_initial)
781 SDLSetWindowScaling(setup.window_scaling_percent);
782 SDL_SetWindowPosition(sdl_window,
783 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
785 video.fullscreen_initial = FALSE;
789 void SDLSetDisplaySize(void)
791 if (sdl_renderer != NULL)
795 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
797 video.display_width = w;
798 video.display_height = h;
801 Error(ERR_DEBUG, "SDL renderer size: %d x %d",
802 video.display_width, video.display_height);
807 SDL_Rect display_bounds;
809 SDL_GetDisplayBounds(0, &display_bounds);
811 video.display_width = display_bounds.w;
812 video.display_height = display_bounds.h;
815 Error(ERR_DEBUG, "SDL display size: %d x %d",
816 video.display_width, video.display_height);
821 void SDLSetScreenSizeAndOffsets(int width, int height)
823 // set default video screen size and offsets
824 video.screen_width = width;
825 video.screen_height = height;
826 video.screen_xoffset = 0;
827 video.screen_yoffset = 0;
829 #if defined(USE_COMPLETE_DISPLAY)
830 float ratio_video = (float) width / height;
831 float ratio_display = (float) video.display_width / video.display_height;
833 if (ratio_video != ratio_display)
835 // adjust drawable screen size to cover the whole device display
837 if (ratio_video < ratio_display)
838 video.screen_width *= ratio_display / ratio_video;
840 video.screen_height *= ratio_video / ratio_display;
842 video.screen_xoffset = (video.screen_width - width) / 2;
843 video.screen_yoffset = (video.screen_height - height) / 2;
846 Error(ERR_DEBUG, "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
848 video.screen_width, video.screen_height,
849 ratio_video, ratio_display);
855 void SDLSetScreenSizeForRenderer(int width, int height)
857 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
860 void SDLSetScreenProperties(void)
863 SDLSetScreenSizeAndOffsets(video.width, video.height);
864 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
867 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
869 video.screen_rendering_mode =
870 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
871 SPECIAL_RENDERING_BITMAP :
872 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
873 SPECIAL_RENDERING_TARGET:
874 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
875 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
878 void SDLSetScreenVsyncMode(char *vsync_mode)
881 (strEqual(vsync_mode, STR_VSYNC_MODE_NORMAL) ? VSYNC_MODE_NORMAL :
882 strEqual(vsync_mode, STR_VSYNC_MODE_ADAPTIVE) ? VSYNC_MODE_ADAPTIVE :
884 int result = SDL_GL_SetSwapInterval(interval);
886 // if adaptive vsync requested, but not supported, retry with normal vsync
887 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
889 interval = VSYNC_MODE_NORMAL;
891 result = SDL_GL_SetSwapInterval(interval);
895 interval = VSYNC_MODE_OFF;
897 video.vsync_mode = interval;
900 void SDLRedrawWindow(void)
902 UpdateScreen_WithoutFrameDelay(NULL);
905 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
908 if (program.headless)
911 SDL_Surface *surface =
912 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
915 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
917 SDLSetNativeSurface(&surface);
919 bitmap->surface = surface;
922 void SDLFreeBitmapPointers(Bitmap *bitmap)
925 SDL_FreeSurface(bitmap->surface);
926 if (bitmap->surface_masked)
927 SDL_FreeSurface(bitmap->surface_masked);
929 bitmap->surface = NULL;
930 bitmap->surface_masked = NULL;
933 SDL_DestroyTexture(bitmap->texture);
934 if (bitmap->texture_masked)
935 SDL_DestroyTexture(bitmap->texture_masked);
937 bitmap->texture = NULL;
938 bitmap->texture_masked = NULL;
941 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
942 int src_x, int src_y, int width, int height,
943 int dst_x, int dst_y, int mask_mode)
945 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
946 SDL_Rect src_rect, dst_rect;
958 // if (src_bitmap != backbuffer || dst_bitmap != window)
959 if (!(src_bitmap == backbuffer && dst_bitmap == window))
960 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
961 src_bitmap->surface_masked : src_bitmap->surface),
962 &src_rect, real_dst_bitmap->surface, &dst_rect);
964 if (dst_bitmap == window)
965 UpdateScreen_WithFrameDelay(&dst_rect);
968 void SDLBlitTexture(Bitmap *bitmap,
969 int src_x, int src_y, int width, int height,
970 int dst_x, int dst_y, int mask_mode)
972 SDL_Texture *texture;
977 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
992 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
995 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
998 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1006 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1008 if (dst_bitmap == window)
1009 UpdateScreen_WithFrameDelay(&rect);
1012 void PrepareFadeBitmap(int draw_target)
1014 Bitmap *fade_bitmap =
1015 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1016 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1018 if (fade_bitmap == NULL)
1021 // copy backbuffer to fading buffer
1022 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1024 // add border and animations to fading buffer
1025 FinalizeScreen(draw_target);
1028 void SDLFadeRectangle(int x, int y, int width, int height,
1029 int fade_mode, int fade_delay, int post_delay,
1030 void (*draw_border_function)(void))
1032 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1033 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1034 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1035 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1036 SDL_Surface *surface_screen = backbuffer->surface;
1037 SDL_Rect src_rect, dst_rect;
1039 int src_x = x, src_y = y;
1040 int dst_x = x, dst_y = y;
1041 unsigned int time_last, time_current;
1043 // store function for drawing global masked border
1044 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1046 // deactivate drawing of global border while fading, if needed
1047 if (draw_border_function == NULL)
1048 gfx.draw_global_border_function = NULL;
1053 src_rect.h = height;
1057 dst_rect.w = width; // (ignored)
1058 dst_rect.h = height; // (ignored)
1060 dst_rect2 = dst_rect;
1062 // before fading in, store backbuffer (without animation graphics)
1063 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1064 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1066 // copy source and target surfaces to temporary surfaces for fading
1067 if (fade_mode & FADE_TYPE_TRANSFORM)
1069 // (source and target fading buffer already prepared)
1071 else if (fade_mode & FADE_TYPE_FADE_IN)
1073 // (target fading buffer already prepared)
1074 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1076 else // FADE_TYPE_FADE_OUT
1078 // (source fading buffer already prepared)
1079 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1082 time_current = SDL_GetTicks();
1084 if (fade_delay <= 0)
1086 // immediately draw final target frame without delay
1087 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1091 // when fading without delay, also skip post delay
1095 if (fade_mode == FADE_MODE_MELT)
1097 boolean done = FALSE;
1098 int melt_pixels = 2;
1099 int melt_columns = width / melt_pixels;
1100 int ypos[melt_columns];
1101 int max_steps = height / 8 + 32;
1106 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1108 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1110 ypos[0] = -GetSimpleRandom(16);
1112 for (i = 1 ; i < melt_columns; i++)
1114 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1116 ypos[i] = ypos[i - 1] + r;
1129 time_last = time_current;
1130 time_current = SDL_GetTicks();
1131 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1132 steps_final = MIN(MAX(0, steps), max_steps);
1136 done = (steps_done >= steps_final);
1138 for (i = 0 ; i < melt_columns; i++)
1146 else if (ypos[i] < height)
1151 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1153 if (ypos[i] + dy >= height)
1154 dy = height - ypos[i];
1156 // copy part of (appearing) target surface to upper area
1157 src_rect.x = src_x + i * melt_pixels;
1158 // src_rect.y = src_y + ypos[i];
1160 src_rect.w = melt_pixels;
1162 src_rect.h = ypos[i] + dy;
1164 dst_rect.x = dst_x + i * melt_pixels;
1165 // dst_rect.y = dst_y + ypos[i];
1168 if (steps_done >= steps_final)
1169 SDL_BlitSurface(surface_target, &src_rect,
1170 surface_screen, &dst_rect);
1174 // copy part of (disappearing) source surface to lower area
1175 src_rect.x = src_x + i * melt_pixels;
1177 src_rect.w = melt_pixels;
1178 src_rect.h = height - ypos[i];
1180 dst_rect.x = dst_x + i * melt_pixels;
1181 dst_rect.y = dst_y + ypos[i];
1183 if (steps_done >= steps_final)
1184 SDL_BlitSurface(surface_source, &src_rect,
1185 surface_screen, &dst_rect);
1191 src_rect.x = src_x + i * melt_pixels;
1193 src_rect.w = melt_pixels;
1194 src_rect.h = height;
1196 dst_rect.x = dst_x + i * melt_pixels;
1199 if (steps_done >= steps_final)
1200 SDL_BlitSurface(surface_target, &src_rect,
1201 surface_screen, &dst_rect);
1205 if (steps_done >= steps_final)
1207 if (draw_border_function != NULL)
1208 draw_border_function();
1210 UpdateScreen_WithFrameDelay(&dst_rect2);
1214 else if (fade_mode == FADE_MODE_CURTAIN)
1218 int xx_size = width / 2;
1220 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1222 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1224 for (xx = 0; xx < xx_size;)
1226 time_last = time_current;
1227 time_current = SDL_GetTicks();
1228 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1229 xx_final = MIN(MAX(0, xx), xx_size);
1234 src_rect.h = height;
1239 // draw new (target) image to screen buffer
1240 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1242 if (xx_final < xx_size)
1244 src_rect.w = xx_size - xx_final;
1245 src_rect.h = height;
1247 // draw old (source) image to screen buffer (left side)
1249 src_rect.x = src_x + xx_final;
1252 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1254 // draw old (source) image to screen buffer (right side)
1256 src_rect.x = src_x + xx_size;
1257 dst_rect.x = dst_x + xx_size + xx_final;
1259 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1262 if (draw_border_function != NULL)
1263 draw_border_function();
1265 // only update the region of the screen that is affected from fading
1266 UpdateScreen_WithFrameDelay(&dst_rect2);
1269 else // fading in, fading out or cross-fading
1274 for (alpha = 0.0; alpha < 255.0;)
1276 time_last = time_current;
1277 time_current = SDL_GetTicks();
1278 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1279 alpha_final = MIN(MAX(0, alpha), 255);
1281 // draw existing (source) image to screen buffer
1282 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1284 // draw new (target) image to screen buffer using alpha blending
1285 SDLSetAlpha(surface_target, TRUE, alpha_final);
1286 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1288 if (draw_border_function != NULL)
1289 draw_border_function();
1291 // only update the region of the screen that is affected from fading
1292 UpdateScreen_WithFrameDelay(&dst_rect);
1297 Delay_WithScreenUpdates(post_delay);
1299 // restore function for drawing global masked border
1300 gfx.draw_global_border_function = draw_global_border_function;
1302 // after fading in, restore backbuffer (without animation graphics)
1303 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1304 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1307 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1308 int to_x, int to_y, Uint32 color)
1310 SDL_Surface *surface = dst_bitmap->surface;
1314 swap_numbers(&from_x, &to_x);
1317 swap_numbers(&from_y, &to_y);
1321 rect.w = (to_x - from_x + 1);
1322 rect.h = (to_y - from_y + 1);
1324 SDL_FillRect(surface, &rect, color);
1327 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1328 int to_x, int to_y, Uint32 color)
1330 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1333 #if ENABLE_UNUSED_CODE
1334 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1335 int num_points, Uint32 color)
1340 for (i = 0; i < num_points - 1; i++)
1342 for (x = 0; x < line_width; x++)
1344 for (y = 0; y < line_width; y++)
1346 int dx = x - line_width / 2;
1347 int dy = y - line_width / 2;
1349 if ((x == 0 && y == 0) ||
1350 (x == 0 && y == line_width - 1) ||
1351 (x == line_width - 1 && y == 0) ||
1352 (x == line_width - 1 && y == line_width - 1))
1355 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1356 points[i+1].x + dx, points[i+1].y + dy, color);
1363 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1365 SDL_Surface *surface = src_bitmap->surface;
1367 switch (surface->format->BytesPerPixel)
1369 case 1: // assuming 8-bpp
1371 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1375 case 2: // probably 15-bpp or 16-bpp
1377 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1381 case 3: // slow 24-bpp mode; usually not used
1384 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1388 shift = surface->format->Rshift;
1389 color |= *(pix + shift / 8) >> shift;
1390 shift = surface->format->Gshift;
1391 color |= *(pix + shift / 8) >> shift;
1392 shift = surface->format->Bshift;
1393 color |= *(pix + shift / 8) >> shift;
1399 case 4: // probably 32-bpp
1401 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1410 // ============================================================================
1411 // The following functions were taken from the SGE library
1412 // (SDL Graphics Extension Library) by Anders Lindström
1413 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1414 // ============================================================================
1416 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1418 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1420 switch (surface->format->BytesPerPixel)
1425 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1431 // Probably 15-bpp or 16-bpp
1432 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1438 // Slow 24-bpp mode, usually not used
1442 // Gack - slow, but endian correct
1443 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1444 shift = surface->format->Rshift;
1445 *(pix+shift/8) = color>>shift;
1446 shift = surface->format->Gshift;
1447 *(pix+shift/8) = color>>shift;
1448 shift = surface->format->Bshift;
1449 *(pix+shift/8) = color>>shift;
1456 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1464 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1465 Uint8 R, Uint8 G, Uint8 B)
1467 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1470 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1472 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1475 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1477 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1480 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1485 // Gack - slow, but endian correct
1486 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1487 shift = surface->format->Rshift;
1488 *(pix+shift/8) = color>>shift;
1489 shift = surface->format->Gshift;
1490 *(pix+shift/8) = color>>shift;
1491 shift = surface->format->Bshift;
1492 *(pix+shift/8) = color>>shift;
1495 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1497 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1500 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1502 switch (dest->format->BytesPerPixel)
1505 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1509 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1513 _PutPixel24(dest,x,y,color);
1517 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1523 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1525 if (SDL_MUSTLOCK(surface))
1527 if (SDL_LockSurface(surface) < 0)
1533 _PutPixel(surface, x, y, color);
1535 if (SDL_MUSTLOCK(surface))
1537 SDL_UnlockSurface(surface);
1542 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1543 Uint8 r, Uint8 g, Uint8 b)
1545 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1548 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1550 if (y >= 0 && y <= dest->h - 1)
1552 switch (dest->format->BytesPerPixel)
1555 return y*dest->pitch;
1559 return y*dest->pitch/2;
1563 return y*dest->pitch;
1567 return y*dest->pitch/4;
1575 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1578 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1580 switch (surface->format->BytesPerPixel)
1585 *((Uint8 *)surface->pixels + ypitch + x) = color;
1591 // Probably 15-bpp or 16-bpp
1592 *((Uint16 *)surface->pixels + ypitch + x) = color;
1598 // Slow 24-bpp mode, usually not used
1602 // Gack - slow, but endian correct
1603 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1604 shift = surface->format->Rshift;
1605 *(pix+shift/8) = color>>shift;
1606 shift = surface->format->Gshift;
1607 *(pix+shift/8) = color>>shift;
1608 shift = surface->format->Bshift;
1609 *(pix+shift/8) = color>>shift;
1616 *((Uint32 *)surface->pixels + ypitch + x) = color;
1623 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1628 if (SDL_MUSTLOCK(Surface))
1630 if (SDL_LockSurface(Surface) < 0)
1644 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1648 if (x2 > Surface->w - 1)
1649 x2 = Surface->w - 1;
1656 SDL_FillRect(Surface, &l, Color);
1658 if (SDL_MUSTLOCK(Surface))
1660 SDL_UnlockSurface(Surface);
1664 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1665 Uint8 R, Uint8 G, Uint8 B)
1667 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1670 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1683 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1687 if (x2 > Surface->w - 1)
1688 x2 = Surface->w - 1;
1695 SDL_FillRect(Surface, &l, Color);
1698 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1703 if (SDL_MUSTLOCK(Surface))
1705 if (SDL_LockSurface(Surface) < 0)
1719 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1723 if (y2 > Surface->h - 1)
1724 y2 = Surface->h - 1;
1731 SDL_FillRect(Surface, &l, Color);
1733 if (SDL_MUSTLOCK(Surface))
1735 SDL_UnlockSurface(Surface);
1739 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1740 Uint8 R, Uint8 G, Uint8 B)
1742 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1745 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1758 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1762 if (y2 > Surface->h - 1)
1763 y2 = Surface->h - 1;
1770 SDL_FillRect(Surface, &l, Color);
1774 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1775 Sint16 x2, Sint16 y2, Uint32 Color,
1776 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1779 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1784 sdx = (dx < 0) ? -1 : 1;
1785 sdy = (dy < 0) ? -1 : 1;
1797 for (x = 0; x < dx; x++)
1799 Callback(Surface, px, py, Color);
1813 for (y = 0; y < dy; y++)
1815 Callback(Surface, px, py, Color);
1830 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1831 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1832 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1835 sge_DoLine(Surface, X1, Y1, X2, Y2,
1836 SDL_MapRGB(Surface->format, R, G, B), Callback);
1840 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1843 if (SDL_MUSTLOCK(Surface))
1845 if (SDL_LockSurface(Surface) < 0)
1850 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1852 // unlock the display
1853 if (SDL_MUSTLOCK(Surface))
1855 SDL_UnlockSurface(Surface);
1860 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1861 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1863 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1867 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1869 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1873 // ----------------------------------------------------------------------------
1874 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1875 // ----------------------------------------------------------------------------
1877 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1878 int width, int height, Uint32 color)
1882 for (y = src_y; y < src_y + height; y++)
1884 for (x = src_x; x < src_x + width; x++)
1886 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1888 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1893 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1894 int src_x, int src_y, int width, int height,
1895 int dst_x, int dst_y)
1899 for (y = 0; y < height; y++)
1901 for (x = 0; x < width; x++)
1903 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1905 if (pixel != BLACK_PIXEL)
1906 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1912 // ============================================================================
1913 // The following functions were taken from the SDL_gfx library version 2.0.3
1914 // (Rotozoomer) by Andreas Schiffler
1915 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1916 // ============================================================================
1918 // ----------------------------------------------------------------------------
1921 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1922 // ----------------------------------------------------------------------------
1932 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1935 tColorRGBA *sp, *csp, *dp;
1939 sp = csp = (tColorRGBA *) src->pixels;
1940 dp = (tColorRGBA *) dst->pixels;
1941 dgap = dst->pitch - dst->w * 4;
1943 for (y = 0; y < dst->h; y++)
1947 for (x = 0; x < dst->w; x++)
1949 tColorRGBA *sp0 = sp;
1950 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1951 tColorRGBA *sp00 = &sp0[0];
1952 tColorRGBA *sp01 = &sp0[1];
1953 tColorRGBA *sp10 = &sp1[0];
1954 tColorRGBA *sp11 = &sp1[1];
1957 // create new color pixel from all four source color pixels
1958 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1959 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1960 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1961 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1966 // advance source pointers
1969 // advance destination pointer
1973 // advance source pointer
1974 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1976 // advance destination pointers
1977 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1983 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1985 int x, y, *sax, *say, *csax, *csay;
1987 tColorRGBA *sp, *csp, *csp0, *dp;
1990 // use specialized zoom function when scaling down to exactly half size
1991 if (src->w == 2 * dst->w &&
1992 src->h == 2 * dst->h)
1993 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1996 sx = (float) src->w / (float) dst->w;
1997 sy = (float) src->h / (float) dst->h;
1999 // allocate memory for row increments
2000 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2001 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2003 // precalculate row increments
2004 for (x = 0; x <= dst->w; x++)
2005 *csax++ = (int)(sx * x);
2007 for (y = 0; y <= dst->h; y++)
2008 *csay++ = (int)(sy * y);
2011 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2012 dp = (tColorRGBA *) dst->pixels;
2013 dgap = dst->pitch - dst->w * 4;
2016 for (y = 0; y < dst->h; y++)
2021 for (x = 0; x < dst->w; x++)
2026 // advance source pointers
2030 // advance destination pointer
2034 // advance source pointer
2036 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2038 // advance destination pointers
2039 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2048 // ----------------------------------------------------------------------------
2051 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2052 // ----------------------------------------------------------------------------
2054 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2056 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2057 Uint8 *sp, *dp, *csp;
2061 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2062 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2064 // allocate memory for row increments
2065 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2066 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2068 // precalculate row increments
2071 for (x = 0; x < dst->w; x++)
2074 *csax = (csx >> 16);
2081 for (y = 0; y < dst->h; y++)
2084 *csay = (csy >> 16);
2091 for (x = 0; x < dst->w; x++)
2099 for (y = 0; y < dst->h; y++)
2106 sp = csp = (Uint8 *) src->pixels;
2107 dp = (Uint8 *) dst->pixels;
2108 dgap = dst->pitch - dst->w;
2112 for (y = 0; y < dst->h; y++)
2116 for (x = 0; x < dst->w; x++)
2121 // advance source pointers
2125 // advance destination pointer
2129 // advance source pointer (for row)
2130 csp += ((*csay) * src->pitch);
2133 // advance destination pointers
2143 // ----------------------------------------------------------------------------
2146 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2147 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2148 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2149 // into a 32bit RGBA format on the fly.
2150 // ----------------------------------------------------------------------------
2152 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2154 SDL_Surface *zoom_src = NULL;
2155 SDL_Surface *zoom_dst = NULL;
2156 boolean is_converted = FALSE;
2163 // determine if source surface is 32 bit or 8 bit
2164 is_32bit = (src->format->BitsPerPixel == 32);
2166 if (is_32bit || src->format->BitsPerPixel == 8)
2168 // use source surface 'as is'
2173 // new source surface is 32 bit with a defined RGB ordering
2174 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2175 0x000000ff, 0x0000ff00, 0x00ff0000,
2176 (src->format->Amask ? 0xff000000 : 0));
2177 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2179 is_converted = TRUE;
2182 // allocate surface to completely contain the zoomed surface
2185 // target surface is 32 bit with source RGBA/ABGR ordering
2186 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2187 zoom_src->format->Rmask,
2188 zoom_src->format->Gmask,
2189 zoom_src->format->Bmask,
2190 zoom_src->format->Amask);
2194 // target surface is 8 bit
2195 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2199 // lock source surface
2200 SDL_LockSurface(zoom_src);
2202 // check which kind of surface we have
2205 // call the 32 bit transformation routine to do the zooming
2206 zoomSurfaceRGBA(zoom_src, zoom_dst);
2211 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2212 zoom_dst->format->palette->colors[i] =
2213 zoom_src->format->palette->colors[i];
2214 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2216 // call the 8 bit transformation routine to do the zooming
2217 zoomSurfaceY(zoom_src, zoom_dst);
2220 // unlock source surface
2221 SDL_UnlockSurface(zoom_src);
2223 // free temporary surface
2225 SDL_FreeSurface(zoom_src);
2227 // return destination surface
2231 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2233 SDL_Surface *new_surface;
2235 if (surface == NULL)
2238 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2239 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2241 // remove alpha channel from native non-transparent surface, if defined
2242 SDLSetAlpha(new_surface, FALSE, 0);
2244 // remove transparent color from native non-transparent surface, if defined
2245 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2250 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2252 Bitmap *dst_bitmap = CreateBitmapStruct();
2253 SDL_Surface *src_surface = src_bitmap->surface_masked;
2254 SDL_Surface *dst_surface;
2256 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2257 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2259 dst_bitmap->width = dst_width;
2260 dst_bitmap->height = dst_height;
2262 // create zoomed temporary surface from source surface
2263 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2265 // create native format destination surface from zoomed temporary surface
2266 SDLSetNativeSurface(&dst_surface);
2268 // set color key for zoomed surface from source surface, if defined
2269 if (SDLHasColorKey(src_surface))
2270 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2271 SDLGetColorKey(src_surface));
2273 // create native non-transparent surface for opaque blitting
2274 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2276 // set native transparent surface for masked blitting
2277 dst_bitmap->surface_masked = dst_surface;
2283 // ============================================================================
2284 // load image to bitmap
2285 // ============================================================================
2287 Bitmap *SDLLoadImage(char *filename)
2289 Bitmap *new_bitmap = CreateBitmapStruct();
2290 SDL_Surface *sdl_image_tmp;
2292 if (program.headless)
2294 // prevent sanity check warnings at later stage
2295 new_bitmap->width = new_bitmap->height = 1;
2300 print_timestamp_init("SDLLoadImage");
2302 print_timestamp_time(getBaseNamePtr(filename));
2304 // load image to temporary surface
2305 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2306 Error(ERR_EXIT, "IMG_Load('%s') failed: %s", getBaseNamePtr(filename),
2309 print_timestamp_time("IMG_Load");
2311 UPDATE_BUSY_STATE();
2313 // create native non-transparent surface for current image
2314 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2315 Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2317 print_timestamp_time("SDLGetNativeSurface (opaque)");
2319 UPDATE_BUSY_STATE();
2321 // set black pixel to transparent if no alpha channel / transparent color
2322 if (!SDLHasAlpha(sdl_image_tmp) &&
2323 !SDLHasColorKey(sdl_image_tmp))
2324 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2325 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2327 // create native transparent surface for current image
2328 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2329 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2331 print_timestamp_time("SDLGetNativeSurface (masked)");
2333 UPDATE_BUSY_STATE();
2335 // free temporary surface
2336 SDL_FreeSurface(sdl_image_tmp);
2338 new_bitmap->width = new_bitmap->surface->w;
2339 new_bitmap->height = new_bitmap->surface->h;
2341 print_timestamp_done("SDLLoadImage");
2347 // ----------------------------------------------------------------------------
2348 // custom cursor fuctions
2349 // ----------------------------------------------------------------------------
2351 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2353 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2354 cursor_info->width, cursor_info->height,
2355 cursor_info->hot_x, cursor_info->hot_y);
2358 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2360 static struct MouseCursorInfo *last_cursor_info = NULL;
2361 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2362 static SDL_Cursor *cursor_default = NULL;
2363 static SDL_Cursor *cursor_current = NULL;
2365 // if invoked for the first time, store the SDL default cursor
2366 if (cursor_default == NULL)
2367 cursor_default = SDL_GetCursor();
2369 // only create new cursor if cursor info (custom only) has changed
2370 if (cursor_info != NULL && cursor_info != last_cursor_info)
2372 cursor_current = create_cursor(cursor_info);
2373 last_cursor_info = cursor_info;
2376 // only set new cursor if cursor info (custom or NULL) has changed
2377 if (cursor_info != last_cursor_info2)
2378 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2380 last_cursor_info2 = cursor_info;
2384 // ============================================================================
2386 // ============================================================================
2388 void SDLOpenAudio(void)
2390 if (program.headless)
2393 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2395 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2399 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2400 AUDIO_NUM_CHANNELS_STEREO,
2401 setup.system.audio_fragment_size) < 0)
2403 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2407 audio.sound_available = TRUE;
2408 audio.music_available = TRUE;
2409 audio.loops_available = TRUE;
2410 audio.sound_enabled = TRUE;
2412 // set number of available mixer channels
2413 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2414 audio.music_channel = MUSIC_CHANNEL;
2415 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2417 Mixer_InitChannels();
2420 void SDLCloseAudio(void)
2423 Mix_HaltChannel(-1);
2426 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2430 // ============================================================================
2432 // ============================================================================
2434 void SDLWaitEvent(Event *event)
2436 SDL_WaitEvent(event);
2439 void SDLCorrectRawMousePosition(int *x, int *y)
2441 if (sdl_renderer == NULL)
2444 // this corrects the raw mouse position for logical screen size within event
2445 // filters (correction done later by SDL library when handling mouse events)
2448 float scale_x, scale_y;
2450 SDL_RenderGetViewport(sdl_renderer, &viewport);
2451 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2453 *x = (int)(*x / scale_x);
2454 *y = (int)(*y / scale_y);
2461 // ============================================================================
2462 // joystick functions
2463 // ============================================================================
2465 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2466 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2467 static int sdl_js_axis[MAX_PLAYERS][2];
2468 static int sdl_js_button[MAX_PLAYERS][2];
2469 static boolean sdl_is_controller[MAX_PLAYERS];
2471 void SDLClearJoystickState(void)
2475 for (i = 0; i < MAX_PLAYERS; i++)
2477 for (j = 0; j < 2; j++)
2479 sdl_js_axis_raw[i][j] = -1;
2480 sdl_js_axis[i][j] = 0;
2481 sdl_js_button[i][j] = 0;
2486 boolean SDLOpenJoystick(int nr)
2488 if (nr < 0 || nr >= MAX_PLAYERS)
2491 sdl_is_controller[nr] = SDL_IsGameController(nr);
2494 Error(ERR_DEBUG, "opening joystick %d (%s)",
2495 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2498 if (sdl_is_controller[nr])
2499 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2501 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2503 return (sdl_joystick[nr] != NULL);
2506 void SDLCloseJoystick(int nr)
2508 if (nr < 0 || nr >= MAX_PLAYERS)
2512 Error(ERR_DEBUG, "closing joystick %d", nr);
2515 if (sdl_is_controller[nr])
2516 SDL_GameControllerClose(sdl_joystick[nr]);
2518 SDL_JoystickClose(sdl_joystick[nr]);
2520 sdl_joystick[nr] = NULL;
2523 boolean SDLCheckJoystickOpened(int nr)
2525 if (nr < 0 || nr >= MAX_PLAYERS)
2528 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2531 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2533 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2534 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2535 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2536 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2538 if (nr < 0 || nr >= MAX_PLAYERS)
2544 // prevent (slightly jittering, but centered) axis A from resetting axis B
2545 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2546 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2549 sdl_js_axis[nr][axis_id] = axis_value;
2550 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2553 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2555 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2556 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2557 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2558 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2559 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2560 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2561 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2562 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2565 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2566 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2567 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2568 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2569 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2570 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2571 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2572 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2574 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2575 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2576 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2577 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2578 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2580 if (nr < 0 || nr >= MAX_PLAYERS)
2583 if (button_id == -1)
2586 sdl_js_button[nr][button_id] = button_state;
2589 void HandleJoystickEvent(Event *event)
2591 // when using joystick, disable overlay touch buttons
2592 runtime.uses_touch_device = FALSE;
2594 switch (event->type)
2596 case SDL_CONTROLLERDEVICEADDED:
2598 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2599 event->cdevice.which);
2604 case SDL_CONTROLLERDEVICEREMOVED:
2606 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2607 event->cdevice.which);
2612 case SDL_CONTROLLERAXISMOTION:
2614 Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2615 event->caxis.which, event->caxis.axis, event->caxis.value);
2617 setJoystickAxis(event->caxis.which,
2619 event->caxis.value);
2622 case SDL_CONTROLLERBUTTONDOWN:
2624 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2625 event->cbutton.which, event->cbutton.button);
2627 setJoystickButton(event->cbutton.which,
2628 event->cbutton.button,
2632 case SDL_CONTROLLERBUTTONUP:
2634 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2635 event->cbutton.which, event->cbutton.button);
2637 setJoystickButton(event->cbutton.which,
2638 event->cbutton.button,
2642 case SDL_JOYAXISMOTION:
2643 if (sdl_is_controller[event->jaxis.which])
2647 Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2648 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2650 if (event->jaxis.axis < 4)
2651 setJoystickAxis(event->jaxis.which,
2653 event->jaxis.value);
2656 case SDL_JOYBUTTONDOWN:
2657 if (sdl_is_controller[event->jaxis.which])
2661 Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2662 event->jbutton.which, event->jbutton.button);
2664 if (event->jbutton.button < 4)
2665 setJoystickButton(event->jbutton.which,
2666 event->jbutton.button,
2670 case SDL_JOYBUTTONUP:
2671 if (sdl_is_controller[event->jaxis.which])
2675 Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2676 event->jbutton.which, event->jbutton.button);
2678 if (event->jbutton.button < 4)
2679 setJoystickButton(event->jbutton.which,
2680 event->jbutton.button,
2689 void SDLInitJoysticks(void)
2691 static boolean sdl_joystick_subsystem_initialized = FALSE;
2692 boolean print_warning = !sdl_joystick_subsystem_initialized;
2693 char *mappings_file_base = getPath2(options.conf_directory,
2694 GAMECONTROLLER_BASENAME);
2695 char *mappings_file_user = getPath2(getUserGameDataDir(),
2696 GAMECONTROLLER_BASENAME);
2700 if (!sdl_joystick_subsystem_initialized)
2702 sdl_joystick_subsystem_initialized = TRUE;
2704 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2706 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2708 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2712 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2714 // the included game controller base mappings should always be found
2715 if (num_mappings == -1)
2716 Error(ERR_WARN, "no game controller base mappings found");
2719 Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2722 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2725 // the personal game controller user mappings may or may not be found
2726 if (num_mappings == -1)
2727 Error(ERR_WARN, "no game controller user mappings found");
2729 Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2731 Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2734 checked_free(mappings_file_base);
2735 checked_free(mappings_file_user);
2738 for (i = 0; i < SDL_NumJoysticks(); i++)
2740 const char *name, *type;
2742 if (SDL_IsGameController(i))
2744 name = SDL_GameControllerNameForIndex(i);
2745 type = "game controller";
2749 name = SDL_JoystickNameForIndex(i);
2753 Error(ERR_INFO, "- joystick %d (%s): '%s'",
2754 i, type, (name ? name : "(Unknown)"));
2759 // assign joysticks from configured to connected joystick for all players
2760 for (i = 0; i < MAX_PLAYERS; i++)
2762 // get configured joystick for this player
2763 char *device_name = setup.input[i].joy.device_name;
2764 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2766 if (joystick_nr >= SDL_NumJoysticks())
2768 if (setup.input[i].use_joystick && print_warning)
2769 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2774 // store configured joystick number for each player
2775 joystick.nr[i] = joystick_nr;
2778 // now open all connected joysticks (regardless if configured or not)
2779 for (i = 0; i < SDL_NumJoysticks(); i++)
2781 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2782 if (SDLCheckJoystickOpened(i))
2783 SDLCloseJoystick(i);
2785 if (SDLOpenJoystick(i))
2786 joystick.status = JOYSTICK_ACTIVATED;
2787 else if (print_warning)
2788 Error(ERR_WARN, "cannot open joystick %d", i);
2791 SDLClearJoystickState();
2794 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2796 if (nr < 0 || nr >= MAX_PLAYERS)
2800 *x = sdl_js_axis[nr][0];
2802 *y = sdl_js_axis[nr][1];
2805 *b1 = sdl_js_button[nr][0];
2807 *b2 = sdl_js_button[nr][1];
2813 // ============================================================================
2814 // touch input overlay functions
2815 // ============================================================================
2817 #if defined(USE_TOUCH_INPUT_OVERLAY)
2818 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2821 int grid_xsize = overlay.grid_xsize;
2822 int grid_ysize = overlay.grid_ysize;
2825 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2826 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2828 for (x = 0; x < grid_xsize; x++)
2830 rect.x = (x + 0) * video.screen_width / grid_xsize;
2831 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2833 for (y = 0; y < grid_ysize; y++)
2835 rect.y = (y + 0) * video.screen_height / grid_ysize;
2836 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2838 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2839 SDL_RenderDrawRect(sdl_renderer, &rect);
2843 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2846 static void RenderFillRectangle(int x, int y, int width, int height)
2848 SDL_Rect rect = { x, y, width, height };
2850 SDL_RenderFillRect(sdl_renderer, &rect);
2853 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2855 static int alpha_direction = 0;
2856 static int alpha_highlight = 0;
2857 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2858 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2860 int grid_xsize = overlay.grid_xsize;
2861 int grid_ysize = overlay.grid_ysize;
2864 if (alpha == alpha_max)
2866 if (alpha_direction < 0)
2868 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2870 if (alpha_highlight == 0)
2871 alpha_direction = 1;
2875 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2877 if (alpha_highlight == alpha_max)
2878 alpha_direction = -1;
2883 alpha_direction = 1;
2884 alpha_highlight = alpha;
2887 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2889 for (x = 0; x < grid_xsize; x++)
2891 for (y = 0; y < grid_ysize; y++)
2893 int grid_button = overlay.grid_button[x][y];
2894 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2895 int alpha_draw = alpha;
2896 int outline_border = MV_NONE;
2897 int border_size = 2;
2898 boolean draw_outlined = setup.touch.draw_outlined;
2899 boolean draw_pressed = setup.touch.draw_pressed;
2901 if (grid_button == CHAR_GRID_BUTTON_NONE)
2904 if (grid_button == overlay.grid_button_highlight)
2906 draw_outlined = FALSE;
2907 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2910 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2913 draw_outlined = FALSE;
2915 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2918 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2920 rect.x = (x + 0) * video.screen_width / grid_xsize;
2921 rect.y = (y + 0) * video.screen_height / grid_ysize;
2922 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2923 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2925 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2927 rect.x += border_size;
2928 rect.w -= border_size;
2930 outline_border |= MV_LEFT;
2933 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2935 rect.w -= border_size;
2937 outline_border |= MV_RIGHT;
2940 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2942 rect.y += border_size;
2943 rect.h -= border_size;
2945 outline_border |= MV_UP;
2948 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2950 rect.h -= border_size;
2952 outline_border |= MV_DOWN;
2957 int rect_x = rect.x +
2958 (outline_border & MV_LEFT ? border_size : 0);
2959 int rect_w = rect.w -
2960 (outline_border & MV_LEFT ? border_size : 0) -
2961 (outline_border & MV_RIGHT ? border_size : 0);
2963 if (outline_border & MV_LEFT)
2964 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2966 if (outline_border & MV_RIGHT)
2967 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2968 border_size, rect.h);
2970 if (outline_border & MV_UP)
2971 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2973 if (outline_border & MV_DOWN)
2974 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
2975 rect_w, border_size);
2979 SDL_RenderFillRect(sdl_renderer, &rect);
2984 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2987 static void DrawTouchInputOverlay(void)
2989 static boolean deactivated = TRUE;
2990 static boolean show_grid = FALSE;
2991 static int alpha = 0;
2992 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2993 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2994 boolean active = (overlay.enabled && overlay.active);
2996 if (!active && deactivated)
3001 if (alpha < alpha_max)
3002 alpha = MIN(alpha + alpha_step, alpha_max);
3004 deactivated = FALSE;
3008 alpha = MAX(0, alpha - alpha_step);
3014 if (overlay.show_grid)
3016 else if (deactivated)
3020 DrawTouchInputOverlay_ShowGrid(alpha);
3022 DrawTouchInputOverlay_ShowGridButtons(alpha);
3025 static void DrawTouchGadgetsOverlay(void)
3027 DrawGadgets_OverlayTouchButtons();