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)
880 int interval = VSYNC_MODE_STR_TO_INT(vsync_mode);
881 int result = SDL_GL_SetSwapInterval(interval);
883 // if adaptive vsync requested, but not supported, retry with normal vsync
884 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
886 interval = VSYNC_MODE_NORMAL;
888 result = SDL_GL_SetSwapInterval(interval);
892 interval = VSYNC_MODE_OFF;
894 video.vsync_mode = interval;
897 void SDLRedrawWindow(void)
899 UpdateScreen_WithoutFrameDelay(NULL);
902 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
905 if (program.headless)
908 SDL_Surface *surface =
909 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
912 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
914 SDLSetNativeSurface(&surface);
916 bitmap->surface = surface;
919 void SDLFreeBitmapPointers(Bitmap *bitmap)
922 SDL_FreeSurface(bitmap->surface);
923 if (bitmap->surface_masked)
924 SDL_FreeSurface(bitmap->surface_masked);
926 bitmap->surface = NULL;
927 bitmap->surface_masked = NULL;
930 SDL_DestroyTexture(bitmap->texture);
931 if (bitmap->texture_masked)
932 SDL_DestroyTexture(bitmap->texture_masked);
934 bitmap->texture = NULL;
935 bitmap->texture_masked = NULL;
938 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
939 int src_x, int src_y, int width, int height,
940 int dst_x, int dst_y, int mask_mode)
942 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
943 SDL_Rect src_rect, dst_rect;
955 // if (src_bitmap != backbuffer || dst_bitmap != window)
956 if (!(src_bitmap == backbuffer && dst_bitmap == window))
957 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
958 src_bitmap->surface_masked : src_bitmap->surface),
959 &src_rect, real_dst_bitmap->surface, &dst_rect);
961 if (dst_bitmap == window)
962 UpdateScreen_WithFrameDelay(&dst_rect);
965 void SDLBlitTexture(Bitmap *bitmap,
966 int src_x, int src_y, int width, int height,
967 int dst_x, int dst_y, int mask_mode)
969 SDL_Texture *texture;
974 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
989 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
992 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
995 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1003 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1005 if (dst_bitmap == window)
1006 UpdateScreen_WithFrameDelay(&rect);
1009 void PrepareFadeBitmap(int draw_target)
1011 Bitmap *fade_bitmap =
1012 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1013 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1015 if (fade_bitmap == NULL)
1018 // copy backbuffer to fading buffer
1019 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1021 // add border and animations to fading buffer
1022 FinalizeScreen(draw_target);
1025 void SDLFadeRectangle(int x, int y, int width, int height,
1026 int fade_mode, int fade_delay, int post_delay,
1027 void (*draw_border_function)(void))
1029 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1030 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1031 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1032 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1033 SDL_Surface *surface_screen = backbuffer->surface;
1034 SDL_Rect src_rect, dst_rect;
1036 int src_x = x, src_y = y;
1037 int dst_x = x, dst_y = y;
1038 unsigned int time_last, time_current;
1040 // store function for drawing global masked border
1041 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1043 // deactivate drawing of global border while fading, if needed
1044 if (draw_border_function == NULL)
1045 gfx.draw_global_border_function = NULL;
1050 src_rect.h = height;
1054 dst_rect.w = width; // (ignored)
1055 dst_rect.h = height; // (ignored)
1057 dst_rect2 = dst_rect;
1059 // before fading in, store backbuffer (without animation graphics)
1060 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1061 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1063 // copy source and target surfaces to temporary surfaces for fading
1064 if (fade_mode & FADE_TYPE_TRANSFORM)
1066 // (source and target fading buffer already prepared)
1068 else if (fade_mode & FADE_TYPE_FADE_IN)
1070 // (target fading buffer already prepared)
1071 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1073 else // FADE_TYPE_FADE_OUT
1075 // (source fading buffer already prepared)
1076 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1079 time_current = SDL_GetTicks();
1081 if (fade_delay <= 0)
1083 // immediately draw final target frame without delay
1084 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1088 // when fading without delay, also skip post delay
1092 if (fade_mode == FADE_MODE_MELT)
1094 boolean done = FALSE;
1095 int melt_pixels = 2;
1096 int melt_columns = width / melt_pixels;
1097 int ypos[melt_columns];
1098 int max_steps = height / 8 + 32;
1103 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1105 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1107 ypos[0] = -GetSimpleRandom(16);
1109 for (i = 1 ; i < melt_columns; i++)
1111 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1113 ypos[i] = ypos[i - 1] + r;
1126 time_last = time_current;
1127 time_current = SDL_GetTicks();
1128 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1129 steps_final = MIN(MAX(0, steps), max_steps);
1133 done = (steps_done >= steps_final);
1135 for (i = 0 ; i < melt_columns; i++)
1143 else if (ypos[i] < height)
1148 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1150 if (ypos[i] + dy >= height)
1151 dy = height - ypos[i];
1153 // copy part of (appearing) target surface to upper area
1154 src_rect.x = src_x + i * melt_pixels;
1155 // src_rect.y = src_y + ypos[i];
1157 src_rect.w = melt_pixels;
1159 src_rect.h = ypos[i] + dy;
1161 dst_rect.x = dst_x + i * melt_pixels;
1162 // dst_rect.y = dst_y + ypos[i];
1165 if (steps_done >= steps_final)
1166 SDL_BlitSurface(surface_target, &src_rect,
1167 surface_screen, &dst_rect);
1171 // copy part of (disappearing) source surface to lower area
1172 src_rect.x = src_x + i * melt_pixels;
1174 src_rect.w = melt_pixels;
1175 src_rect.h = height - ypos[i];
1177 dst_rect.x = dst_x + i * melt_pixels;
1178 dst_rect.y = dst_y + ypos[i];
1180 if (steps_done >= steps_final)
1181 SDL_BlitSurface(surface_source, &src_rect,
1182 surface_screen, &dst_rect);
1188 src_rect.x = src_x + i * melt_pixels;
1190 src_rect.w = melt_pixels;
1191 src_rect.h = height;
1193 dst_rect.x = dst_x + i * melt_pixels;
1196 if (steps_done >= steps_final)
1197 SDL_BlitSurface(surface_target, &src_rect,
1198 surface_screen, &dst_rect);
1202 if (steps_done >= steps_final)
1204 if (draw_border_function != NULL)
1205 draw_border_function();
1207 UpdateScreen_WithFrameDelay(&dst_rect2);
1211 else if (fade_mode == FADE_MODE_CURTAIN)
1215 int xx_size = width / 2;
1217 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1219 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1221 for (xx = 0; xx < xx_size;)
1223 time_last = time_current;
1224 time_current = SDL_GetTicks();
1225 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1226 xx_final = MIN(MAX(0, xx), xx_size);
1231 src_rect.h = height;
1236 // draw new (target) image to screen buffer
1237 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1239 if (xx_final < xx_size)
1241 src_rect.w = xx_size - xx_final;
1242 src_rect.h = height;
1244 // draw old (source) image to screen buffer (left side)
1246 src_rect.x = src_x + xx_final;
1249 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1251 // draw old (source) image to screen buffer (right side)
1253 src_rect.x = src_x + xx_size;
1254 dst_rect.x = dst_x + xx_size + xx_final;
1256 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1259 if (draw_border_function != NULL)
1260 draw_border_function();
1262 // only update the region of the screen that is affected from fading
1263 UpdateScreen_WithFrameDelay(&dst_rect2);
1266 else // fading in, fading out or cross-fading
1271 for (alpha = 0.0; alpha < 255.0;)
1273 time_last = time_current;
1274 time_current = SDL_GetTicks();
1275 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1276 alpha_final = MIN(MAX(0, alpha), 255);
1278 // draw existing (source) image to screen buffer
1279 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1281 // draw new (target) image to screen buffer using alpha blending
1282 SDLSetAlpha(surface_target, TRUE, alpha_final);
1283 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1285 if (draw_border_function != NULL)
1286 draw_border_function();
1288 // only update the region of the screen that is affected from fading
1289 UpdateScreen_WithFrameDelay(&dst_rect);
1294 Delay_WithScreenUpdates(post_delay);
1296 // restore function for drawing global masked border
1297 gfx.draw_global_border_function = draw_global_border_function;
1299 // after fading in, restore backbuffer (without animation graphics)
1300 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1301 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1304 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1305 int to_x, int to_y, Uint32 color)
1307 SDL_Surface *surface = dst_bitmap->surface;
1311 swap_numbers(&from_x, &to_x);
1314 swap_numbers(&from_y, &to_y);
1318 rect.w = (to_x - from_x + 1);
1319 rect.h = (to_y - from_y + 1);
1321 SDL_FillRect(surface, &rect, color);
1324 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1325 int to_x, int to_y, Uint32 color)
1327 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1330 #if ENABLE_UNUSED_CODE
1331 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1332 int num_points, Uint32 color)
1337 for (i = 0; i < num_points - 1; i++)
1339 for (x = 0; x < line_width; x++)
1341 for (y = 0; y < line_width; y++)
1343 int dx = x - line_width / 2;
1344 int dy = y - line_width / 2;
1346 if ((x == 0 && y == 0) ||
1347 (x == 0 && y == line_width - 1) ||
1348 (x == line_width - 1 && y == 0) ||
1349 (x == line_width - 1 && y == line_width - 1))
1352 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1353 points[i+1].x + dx, points[i+1].y + dy, color);
1360 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1362 SDL_Surface *surface = src_bitmap->surface;
1364 switch (surface->format->BytesPerPixel)
1366 case 1: // assuming 8-bpp
1368 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1372 case 2: // probably 15-bpp or 16-bpp
1374 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1378 case 3: // slow 24-bpp mode; usually not used
1381 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1385 shift = surface->format->Rshift;
1386 color |= *(pix + shift / 8) >> shift;
1387 shift = surface->format->Gshift;
1388 color |= *(pix + shift / 8) >> shift;
1389 shift = surface->format->Bshift;
1390 color |= *(pix + shift / 8) >> shift;
1396 case 4: // probably 32-bpp
1398 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1407 // ============================================================================
1408 // The following functions were taken from the SGE library
1409 // (SDL Graphics Extension Library) by Anders Lindström
1410 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1411 // ============================================================================
1413 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1415 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1417 switch (surface->format->BytesPerPixel)
1422 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1428 // Probably 15-bpp or 16-bpp
1429 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1435 // Slow 24-bpp mode, usually not used
1439 // Gack - slow, but endian correct
1440 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1441 shift = surface->format->Rshift;
1442 *(pix+shift/8) = color>>shift;
1443 shift = surface->format->Gshift;
1444 *(pix+shift/8) = color>>shift;
1445 shift = surface->format->Bshift;
1446 *(pix+shift/8) = color>>shift;
1453 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1461 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1462 Uint8 R, Uint8 G, Uint8 B)
1464 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1467 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1469 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1472 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1474 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1477 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1482 // Gack - slow, but endian correct
1483 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1484 shift = surface->format->Rshift;
1485 *(pix+shift/8) = color>>shift;
1486 shift = surface->format->Gshift;
1487 *(pix+shift/8) = color>>shift;
1488 shift = surface->format->Bshift;
1489 *(pix+shift/8) = color>>shift;
1492 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1494 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1497 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1499 switch (dest->format->BytesPerPixel)
1502 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1506 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1510 _PutPixel24(dest,x,y,color);
1514 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1520 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1522 if (SDL_MUSTLOCK(surface))
1524 if (SDL_LockSurface(surface) < 0)
1530 _PutPixel(surface, x, y, color);
1532 if (SDL_MUSTLOCK(surface))
1534 SDL_UnlockSurface(surface);
1539 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1540 Uint8 r, Uint8 g, Uint8 b)
1542 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1545 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1547 if (y >= 0 && y <= dest->h - 1)
1549 switch (dest->format->BytesPerPixel)
1552 return y*dest->pitch;
1556 return y*dest->pitch/2;
1560 return y*dest->pitch;
1564 return y*dest->pitch/4;
1572 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1575 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1577 switch (surface->format->BytesPerPixel)
1582 *((Uint8 *)surface->pixels + ypitch + x) = color;
1588 // Probably 15-bpp or 16-bpp
1589 *((Uint16 *)surface->pixels + ypitch + x) = color;
1595 // Slow 24-bpp mode, usually not used
1599 // Gack - slow, but endian correct
1600 pix = (Uint8 *)surface->pixels + ypitch + 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 + ypitch + x) = color;
1620 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1625 if (SDL_MUSTLOCK(Surface))
1627 if (SDL_LockSurface(Surface) < 0)
1641 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1645 if (x2 > Surface->w - 1)
1646 x2 = Surface->w - 1;
1653 SDL_FillRect(Surface, &l, Color);
1655 if (SDL_MUSTLOCK(Surface))
1657 SDL_UnlockSurface(Surface);
1661 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1662 Uint8 R, Uint8 G, Uint8 B)
1664 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1667 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1680 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1684 if (x2 > Surface->w - 1)
1685 x2 = Surface->w - 1;
1692 SDL_FillRect(Surface, &l, Color);
1695 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1700 if (SDL_MUSTLOCK(Surface))
1702 if (SDL_LockSurface(Surface) < 0)
1716 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1720 if (y2 > Surface->h - 1)
1721 y2 = Surface->h - 1;
1728 SDL_FillRect(Surface, &l, Color);
1730 if (SDL_MUSTLOCK(Surface))
1732 SDL_UnlockSurface(Surface);
1736 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1737 Uint8 R, Uint8 G, Uint8 B)
1739 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1742 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1755 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1759 if (y2 > Surface->h - 1)
1760 y2 = Surface->h - 1;
1767 SDL_FillRect(Surface, &l, Color);
1771 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1772 Sint16 x2, Sint16 y2, Uint32 Color,
1773 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1776 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1781 sdx = (dx < 0) ? -1 : 1;
1782 sdy = (dy < 0) ? -1 : 1;
1794 for (x = 0; x < dx; x++)
1796 Callback(Surface, px, py, Color);
1810 for (y = 0; y < dy; y++)
1812 Callback(Surface, px, py, Color);
1827 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1828 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1829 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1832 sge_DoLine(Surface, X1, Y1, X2, Y2,
1833 SDL_MapRGB(Surface->format, R, G, B), Callback);
1837 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1840 if (SDL_MUSTLOCK(Surface))
1842 if (SDL_LockSurface(Surface) < 0)
1847 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1849 // unlock the display
1850 if (SDL_MUSTLOCK(Surface))
1852 SDL_UnlockSurface(Surface);
1857 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1858 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1860 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1864 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1866 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1870 // ----------------------------------------------------------------------------
1871 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1872 // ----------------------------------------------------------------------------
1874 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1875 int width, int height, Uint32 color)
1879 for (y = src_y; y < src_y + height; y++)
1881 for (x = src_x; x < src_x + width; x++)
1883 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1885 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1890 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1891 int src_x, int src_y, int width, int height,
1892 int dst_x, int dst_y)
1896 for (y = 0; y < height; y++)
1898 for (x = 0; x < width; x++)
1900 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1902 if (pixel != BLACK_PIXEL)
1903 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1909 // ============================================================================
1910 // The following functions were taken from the SDL_gfx library version 2.0.3
1911 // (Rotozoomer) by Andreas Schiffler
1912 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1913 // ============================================================================
1915 // ----------------------------------------------------------------------------
1918 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1919 // ----------------------------------------------------------------------------
1929 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1932 tColorRGBA *sp, *csp, *dp;
1936 sp = csp = (tColorRGBA *) src->pixels;
1937 dp = (tColorRGBA *) dst->pixels;
1938 dgap = dst->pitch - dst->w * 4;
1940 for (y = 0; y < dst->h; y++)
1944 for (x = 0; x < dst->w; x++)
1946 tColorRGBA *sp0 = sp;
1947 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1948 tColorRGBA *sp00 = &sp0[0];
1949 tColorRGBA *sp01 = &sp0[1];
1950 tColorRGBA *sp10 = &sp1[0];
1951 tColorRGBA *sp11 = &sp1[1];
1954 // create new color pixel from all four source color pixels
1955 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1956 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1957 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1958 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1963 // advance source pointers
1966 // advance destination pointer
1970 // advance source pointer
1971 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1973 // advance destination pointers
1974 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1980 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1982 int x, y, *sax, *say, *csax, *csay;
1984 tColorRGBA *sp, *csp, *csp0, *dp;
1987 // use specialized zoom function when scaling down to exactly half size
1988 if (src->w == 2 * dst->w &&
1989 src->h == 2 * dst->h)
1990 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1993 sx = (float) src->w / (float) dst->w;
1994 sy = (float) src->h / (float) dst->h;
1996 // allocate memory for row increments
1997 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1998 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2000 // precalculate row increments
2001 for (x = 0; x <= dst->w; x++)
2002 *csax++ = (int)(sx * x);
2004 for (y = 0; y <= dst->h; y++)
2005 *csay++ = (int)(sy * y);
2008 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2009 dp = (tColorRGBA *) dst->pixels;
2010 dgap = dst->pitch - dst->w * 4;
2013 for (y = 0; y < dst->h; y++)
2018 for (x = 0; x < dst->w; x++)
2023 // advance source pointers
2027 // advance destination pointer
2031 // advance source pointer
2033 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2035 // advance destination pointers
2036 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2045 // ----------------------------------------------------------------------------
2048 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2049 // ----------------------------------------------------------------------------
2051 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2053 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2054 Uint8 *sp, *dp, *csp;
2058 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2059 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2061 // allocate memory for row increments
2062 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2063 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2065 // precalculate row increments
2068 for (x = 0; x < dst->w; x++)
2071 *csax = (csx >> 16);
2078 for (y = 0; y < dst->h; y++)
2081 *csay = (csy >> 16);
2088 for (x = 0; x < dst->w; x++)
2096 for (y = 0; y < dst->h; y++)
2103 sp = csp = (Uint8 *) src->pixels;
2104 dp = (Uint8 *) dst->pixels;
2105 dgap = dst->pitch - dst->w;
2109 for (y = 0; y < dst->h; y++)
2113 for (x = 0; x < dst->w; x++)
2118 // advance source pointers
2122 // advance destination pointer
2126 // advance source pointer (for row)
2127 csp += ((*csay) * src->pitch);
2130 // advance destination pointers
2140 // ----------------------------------------------------------------------------
2143 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2144 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2145 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2146 // into a 32bit RGBA format on the fly.
2147 // ----------------------------------------------------------------------------
2149 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2151 SDL_Surface *zoom_src = NULL;
2152 SDL_Surface *zoom_dst = NULL;
2153 boolean is_converted = FALSE;
2160 // determine if source surface is 32 bit or 8 bit
2161 is_32bit = (src->format->BitsPerPixel == 32);
2163 if (is_32bit || src->format->BitsPerPixel == 8)
2165 // use source surface 'as is'
2170 // new source surface is 32 bit with a defined RGB ordering
2171 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2172 0x000000ff, 0x0000ff00, 0x00ff0000,
2173 (src->format->Amask ? 0xff000000 : 0));
2174 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2176 is_converted = TRUE;
2179 // allocate surface to completely contain the zoomed surface
2182 // target surface is 32 bit with source RGBA/ABGR ordering
2183 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2184 zoom_src->format->Rmask,
2185 zoom_src->format->Gmask,
2186 zoom_src->format->Bmask,
2187 zoom_src->format->Amask);
2191 // target surface is 8 bit
2192 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2196 // lock source surface
2197 SDL_LockSurface(zoom_src);
2199 // check which kind of surface we have
2202 // call the 32 bit transformation routine to do the zooming
2203 zoomSurfaceRGBA(zoom_src, zoom_dst);
2208 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2209 zoom_dst->format->palette->colors[i] =
2210 zoom_src->format->palette->colors[i];
2211 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2213 // call the 8 bit transformation routine to do the zooming
2214 zoomSurfaceY(zoom_src, zoom_dst);
2217 // unlock source surface
2218 SDL_UnlockSurface(zoom_src);
2220 // free temporary surface
2222 SDL_FreeSurface(zoom_src);
2224 // return destination surface
2228 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2230 SDL_Surface *new_surface;
2232 if (surface == NULL)
2235 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2236 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2238 // remove alpha channel from native non-transparent surface, if defined
2239 SDLSetAlpha(new_surface, FALSE, 0);
2241 // remove transparent color from native non-transparent surface, if defined
2242 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2247 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2249 Bitmap *dst_bitmap = CreateBitmapStruct();
2250 SDL_Surface *src_surface = src_bitmap->surface_masked;
2251 SDL_Surface *dst_surface;
2253 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2254 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2256 dst_bitmap->width = dst_width;
2257 dst_bitmap->height = dst_height;
2259 // create zoomed temporary surface from source surface
2260 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2262 // create native format destination surface from zoomed temporary surface
2263 SDLSetNativeSurface(&dst_surface);
2265 // set color key for zoomed surface from source surface, if defined
2266 if (SDLHasColorKey(src_surface))
2267 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2268 SDLGetColorKey(src_surface));
2270 // create native non-transparent surface for opaque blitting
2271 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2273 // set native transparent surface for masked blitting
2274 dst_bitmap->surface_masked = dst_surface;
2280 // ============================================================================
2281 // load image to bitmap
2282 // ============================================================================
2284 Bitmap *SDLLoadImage(char *filename)
2286 Bitmap *new_bitmap = CreateBitmapStruct();
2287 SDL_Surface *sdl_image_tmp;
2289 if (program.headless)
2291 // prevent sanity check warnings at later stage
2292 new_bitmap->width = new_bitmap->height = 1;
2297 print_timestamp_init("SDLLoadImage");
2299 print_timestamp_time(getBaseNamePtr(filename));
2301 // load image to temporary surface
2302 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2303 Error(ERR_EXIT, "IMG_Load('%s') failed: %s", getBaseNamePtr(filename),
2306 print_timestamp_time("IMG_Load");
2308 UPDATE_BUSY_STATE();
2310 // create native non-transparent surface for current image
2311 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2312 Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2314 print_timestamp_time("SDLGetNativeSurface (opaque)");
2316 UPDATE_BUSY_STATE();
2318 // set black pixel to transparent if no alpha channel / transparent color
2319 if (!SDLHasAlpha(sdl_image_tmp) &&
2320 !SDLHasColorKey(sdl_image_tmp))
2321 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2322 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2324 // create native transparent surface for current image
2325 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2326 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2328 print_timestamp_time("SDLGetNativeSurface (masked)");
2330 UPDATE_BUSY_STATE();
2332 // free temporary surface
2333 SDL_FreeSurface(sdl_image_tmp);
2335 new_bitmap->width = new_bitmap->surface->w;
2336 new_bitmap->height = new_bitmap->surface->h;
2338 print_timestamp_done("SDLLoadImage");
2344 // ----------------------------------------------------------------------------
2345 // custom cursor fuctions
2346 // ----------------------------------------------------------------------------
2348 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2350 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2351 cursor_info->width, cursor_info->height,
2352 cursor_info->hot_x, cursor_info->hot_y);
2355 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2357 static struct MouseCursorInfo *last_cursor_info = NULL;
2358 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2359 static SDL_Cursor *cursor_default = NULL;
2360 static SDL_Cursor *cursor_current = NULL;
2362 // if invoked for the first time, store the SDL default cursor
2363 if (cursor_default == NULL)
2364 cursor_default = SDL_GetCursor();
2366 // only create new cursor if cursor info (custom only) has changed
2367 if (cursor_info != NULL && cursor_info != last_cursor_info)
2369 cursor_current = create_cursor(cursor_info);
2370 last_cursor_info = cursor_info;
2373 // only set new cursor if cursor info (custom or NULL) has changed
2374 if (cursor_info != last_cursor_info2)
2375 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2377 last_cursor_info2 = cursor_info;
2381 // ============================================================================
2383 // ============================================================================
2385 void SDLOpenAudio(void)
2387 if (program.headless)
2390 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2392 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2396 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2397 AUDIO_NUM_CHANNELS_STEREO,
2398 setup.system.audio_fragment_size) < 0)
2400 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2404 audio.sound_available = TRUE;
2405 audio.music_available = TRUE;
2406 audio.loops_available = TRUE;
2407 audio.sound_enabled = TRUE;
2409 // set number of available mixer channels
2410 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2411 audio.music_channel = MUSIC_CHANNEL;
2412 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2414 Mixer_InitChannels();
2417 void SDLCloseAudio(void)
2420 Mix_HaltChannel(-1);
2423 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2427 // ============================================================================
2429 // ============================================================================
2431 void SDLWaitEvent(Event *event)
2433 SDL_WaitEvent(event);
2436 void SDLCorrectRawMousePosition(int *x, int *y)
2438 if (sdl_renderer == NULL)
2441 // this corrects the raw mouse position for logical screen size within event
2442 // filters (correction done later by SDL library when handling mouse events)
2445 float scale_x, scale_y;
2447 SDL_RenderGetViewport(sdl_renderer, &viewport);
2448 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2450 *x = (int)(*x / scale_x);
2451 *y = (int)(*y / scale_y);
2458 // ============================================================================
2459 // joystick functions
2460 // ============================================================================
2462 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2463 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2464 static int sdl_js_axis[MAX_PLAYERS][2];
2465 static int sdl_js_button[MAX_PLAYERS][2];
2466 static boolean sdl_is_controller[MAX_PLAYERS];
2468 void SDLClearJoystickState(void)
2472 for (i = 0; i < MAX_PLAYERS; i++)
2474 for (j = 0; j < 2; j++)
2476 sdl_js_axis_raw[i][j] = -1;
2477 sdl_js_axis[i][j] = 0;
2478 sdl_js_button[i][j] = 0;
2483 boolean SDLOpenJoystick(int nr)
2485 if (nr < 0 || nr >= MAX_PLAYERS)
2488 sdl_is_controller[nr] = SDL_IsGameController(nr);
2491 Error(ERR_DEBUG, "opening joystick %d (%s)",
2492 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2495 if (sdl_is_controller[nr])
2496 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2498 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2500 return (sdl_joystick[nr] != NULL);
2503 void SDLCloseJoystick(int nr)
2505 if (nr < 0 || nr >= MAX_PLAYERS)
2509 Error(ERR_DEBUG, "closing joystick %d", nr);
2512 if (sdl_is_controller[nr])
2513 SDL_GameControllerClose(sdl_joystick[nr]);
2515 SDL_JoystickClose(sdl_joystick[nr]);
2517 sdl_joystick[nr] = NULL;
2520 boolean SDLCheckJoystickOpened(int nr)
2522 if (nr < 0 || nr >= MAX_PLAYERS)
2525 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2528 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2530 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2531 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2532 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2533 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2535 if (nr < 0 || nr >= MAX_PLAYERS)
2541 // prevent (slightly jittering, but centered) axis A from resetting axis B
2542 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2543 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2546 sdl_js_axis[nr][axis_id] = axis_value;
2547 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2550 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2552 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2553 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2554 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2555 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2556 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2557 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2558 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2559 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2562 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2563 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2564 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2565 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2566 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2567 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2568 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2569 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2571 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2572 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2573 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2574 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2575 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2577 if (nr < 0 || nr >= MAX_PLAYERS)
2580 if (button_id == -1)
2583 sdl_js_button[nr][button_id] = button_state;
2586 void HandleJoystickEvent(Event *event)
2588 // when using joystick, disable overlay touch buttons
2589 runtime.uses_touch_device = FALSE;
2591 switch (event->type)
2593 case SDL_CONTROLLERDEVICEADDED:
2595 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2596 event->cdevice.which);
2601 case SDL_CONTROLLERDEVICEREMOVED:
2603 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2604 event->cdevice.which);
2609 case SDL_CONTROLLERAXISMOTION:
2611 Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2612 event->caxis.which, event->caxis.axis, event->caxis.value);
2614 setJoystickAxis(event->caxis.which,
2616 event->caxis.value);
2619 case SDL_CONTROLLERBUTTONDOWN:
2621 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2622 event->cbutton.which, event->cbutton.button);
2624 setJoystickButton(event->cbutton.which,
2625 event->cbutton.button,
2629 case SDL_CONTROLLERBUTTONUP:
2631 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2632 event->cbutton.which, event->cbutton.button);
2634 setJoystickButton(event->cbutton.which,
2635 event->cbutton.button,
2639 case SDL_JOYAXISMOTION:
2640 if (sdl_is_controller[event->jaxis.which])
2644 Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2645 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2647 if (event->jaxis.axis < 4)
2648 setJoystickAxis(event->jaxis.which,
2650 event->jaxis.value);
2653 case SDL_JOYBUTTONDOWN:
2654 if (sdl_is_controller[event->jaxis.which])
2658 Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2659 event->jbutton.which, event->jbutton.button);
2661 if (event->jbutton.button < 4)
2662 setJoystickButton(event->jbutton.which,
2663 event->jbutton.button,
2667 case SDL_JOYBUTTONUP:
2668 if (sdl_is_controller[event->jaxis.which])
2672 Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2673 event->jbutton.which, event->jbutton.button);
2675 if (event->jbutton.button < 4)
2676 setJoystickButton(event->jbutton.which,
2677 event->jbutton.button,
2686 void SDLInitJoysticks(void)
2688 static boolean sdl_joystick_subsystem_initialized = FALSE;
2689 boolean print_warning = !sdl_joystick_subsystem_initialized;
2690 char *mappings_file_base = getPath2(options.conf_directory,
2691 GAMECONTROLLER_BASENAME);
2692 char *mappings_file_user = getPath2(getUserGameDataDir(),
2693 GAMECONTROLLER_BASENAME);
2697 if (!sdl_joystick_subsystem_initialized)
2699 sdl_joystick_subsystem_initialized = TRUE;
2701 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2703 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2705 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2709 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2711 // the included game controller base mappings should always be found
2712 if (num_mappings == -1)
2713 Error(ERR_WARN, "no game controller base mappings found");
2716 Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2719 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2722 // the personal game controller user mappings may or may not be found
2723 if (num_mappings == -1)
2724 Error(ERR_WARN, "no game controller user mappings found");
2726 Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2728 Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2731 checked_free(mappings_file_base);
2732 checked_free(mappings_file_user);
2735 for (i = 0; i < SDL_NumJoysticks(); i++)
2737 const char *name, *type;
2739 if (SDL_IsGameController(i))
2741 name = SDL_GameControllerNameForIndex(i);
2742 type = "game controller";
2746 name = SDL_JoystickNameForIndex(i);
2750 Error(ERR_INFO, "- joystick %d (%s): '%s'",
2751 i, type, (name ? name : "(Unknown)"));
2756 // assign joysticks from configured to connected joystick for all players
2757 for (i = 0; i < MAX_PLAYERS; i++)
2759 // get configured joystick for this player
2760 char *device_name = setup.input[i].joy.device_name;
2761 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2763 if (joystick_nr >= SDL_NumJoysticks())
2765 if (setup.input[i].use_joystick && print_warning)
2766 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2771 // store configured joystick number for each player
2772 joystick.nr[i] = joystick_nr;
2775 // now open all connected joysticks (regardless if configured or not)
2776 for (i = 0; i < SDL_NumJoysticks(); i++)
2778 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2779 if (SDLCheckJoystickOpened(i))
2780 SDLCloseJoystick(i);
2782 if (SDLOpenJoystick(i))
2783 joystick.status = JOYSTICK_ACTIVATED;
2784 else if (print_warning)
2785 Error(ERR_WARN, "cannot open joystick %d", i);
2788 SDLClearJoystickState();
2791 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2793 if (nr < 0 || nr >= MAX_PLAYERS)
2797 *x = sdl_js_axis[nr][0];
2799 *y = sdl_js_axis[nr][1];
2802 *b1 = sdl_js_button[nr][0];
2804 *b2 = sdl_js_button[nr][1];
2810 // ============================================================================
2811 // touch input overlay functions
2812 // ============================================================================
2814 #if defined(USE_TOUCH_INPUT_OVERLAY)
2815 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2818 int grid_xsize = overlay.grid_xsize;
2819 int grid_ysize = overlay.grid_ysize;
2822 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2823 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2825 for (x = 0; x < grid_xsize; x++)
2827 rect.x = (x + 0) * video.screen_width / grid_xsize;
2828 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2830 for (y = 0; y < grid_ysize; y++)
2832 rect.y = (y + 0) * video.screen_height / grid_ysize;
2833 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2835 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2836 SDL_RenderDrawRect(sdl_renderer, &rect);
2840 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2843 static void RenderFillRectangle(int x, int y, int width, int height)
2845 SDL_Rect rect = { x, y, width, height };
2847 SDL_RenderFillRect(sdl_renderer, &rect);
2850 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2852 static int alpha_direction = 0;
2853 static int alpha_highlight = 0;
2854 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2855 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2857 int grid_xsize = overlay.grid_xsize;
2858 int grid_ysize = overlay.grid_ysize;
2861 if (alpha == alpha_max)
2863 if (alpha_direction < 0)
2865 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2867 if (alpha_highlight == 0)
2868 alpha_direction = 1;
2872 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2874 if (alpha_highlight == alpha_max)
2875 alpha_direction = -1;
2880 alpha_direction = 1;
2881 alpha_highlight = alpha;
2884 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2886 for (x = 0; x < grid_xsize; x++)
2888 for (y = 0; y < grid_ysize; y++)
2890 int grid_button = overlay.grid_button[x][y];
2891 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2892 int alpha_draw = alpha;
2893 int outline_border = MV_NONE;
2894 int border_size = 2;
2895 boolean draw_outlined = setup.touch.draw_outlined;
2896 boolean draw_pressed = setup.touch.draw_pressed;
2898 if (grid_button == CHAR_GRID_BUTTON_NONE)
2901 if (grid_button == overlay.grid_button_highlight)
2903 draw_outlined = FALSE;
2904 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2907 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2910 draw_outlined = FALSE;
2912 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2915 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2917 rect.x = (x + 0) * video.screen_width / grid_xsize;
2918 rect.y = (y + 0) * video.screen_height / grid_ysize;
2919 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2920 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2922 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2924 rect.x += border_size;
2925 rect.w -= border_size;
2927 outline_border |= MV_LEFT;
2930 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2932 rect.w -= border_size;
2934 outline_border |= MV_RIGHT;
2937 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2939 rect.y += border_size;
2940 rect.h -= border_size;
2942 outline_border |= MV_UP;
2945 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2947 rect.h -= border_size;
2949 outline_border |= MV_DOWN;
2954 int rect_x = rect.x +
2955 (outline_border & MV_LEFT ? border_size : 0);
2956 int rect_w = rect.w -
2957 (outline_border & MV_LEFT ? border_size : 0) -
2958 (outline_border & MV_RIGHT ? border_size : 0);
2960 if (outline_border & MV_LEFT)
2961 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2963 if (outline_border & MV_RIGHT)
2964 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2965 border_size, rect.h);
2967 if (outline_border & MV_UP)
2968 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2970 if (outline_border & MV_DOWN)
2971 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
2972 rect_w, border_size);
2976 SDL_RenderFillRect(sdl_renderer, &rect);
2981 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2984 static void DrawTouchInputOverlay(void)
2986 static boolean deactivated = TRUE;
2987 static boolean show_grid = FALSE;
2988 static int alpha = 0;
2989 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2990 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2991 boolean active = (overlay.enabled && overlay.active);
2993 if (!active && deactivated)
2998 if (alpha < alpha_max)
2999 alpha = MIN(alpha + alpha_step, alpha_max);
3001 deactivated = FALSE;
3005 alpha = MAX(0, alpha - alpha_step);
3011 if (overlay.show_grid)
3013 else if (deactivated)
3017 DrawTouchInputOverlay_ShowGrid(alpha);
3019 DrawTouchInputOverlay_ShowGridButtons(alpha);
3022 static void DrawTouchGadgetsOverlay(void)
3024 DrawGadgets_OverlayTouchButtons();