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 Debug("internal:frame", "FrameCounter == %d [%s]", 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 Warn("SDLSetWindowIcon(): cannot find file '%s'", basename);
274 if ((surface = IMG_Load(filename)) == NULL)
276 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 Fail("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 Fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
409 void SDLCreateBitmapTextures(Bitmap *bitmap)
415 SDL_DestroyTexture(bitmap->texture);
416 if (bitmap->texture_masked)
417 SDL_DestroyTexture(bitmap->texture_masked);
419 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
420 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
423 void SDLFreeBitmapTextures(Bitmap *bitmap)
429 SDL_DestroyTexture(bitmap->texture);
430 if (bitmap->texture_masked)
431 SDL_DestroyTexture(bitmap->texture_masked);
433 bitmap->texture = NULL;
434 bitmap->texture_masked = NULL;
437 void SDLInitVideoDisplay(void)
439 // set hint to select render driver as specified in setup config file
440 if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
441 SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
443 // initialize SDL video
444 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
445 Fail("SDL_InitSubSystem() failed: %s", SDL_GetError());
447 // set default SDL depth
448 video.default_depth = 32; // (how to determine video depth in SDL2?)
450 // Code used with SDL 1.2:
451 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
454 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
456 if (program.headless)
459 video.window_scaling_percent = setup.window_scaling_percent;
460 video.window_scaling_quality = setup.window_scaling_quality;
462 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
464 // SDL 2.0: support for (desktop) fullscreen mode available
465 video.fullscreen_available = TRUE;
467 // open SDL video output device (window or fullscreen mode)
468 if (!SDLSetVideoMode(fullscreen))
469 Fail("setting video mode failed");
471 // !!! SDL2 can only set the window icon if the window already exists !!!
473 SDLSetWindowIcon(program.icon_filename);
475 // set window and icon title
479 static void SDLInitVideoBuffer_DrawBuffer(void)
481 /* SDL cannot directly draw to the visible video framebuffer like X11,
482 but always uses a backbuffer, which is then blitted to the visible
483 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
484 visible video framebuffer with 'SDL_Flip', if the hardware supports
485 this). Therefore do not use an additional backbuffer for drawing, but
486 use a symbolic buffer (distinguishable from the SDL backbuffer) called
487 'window', which indicates that the SDL backbuffer should be updated to
488 the visible video framebuffer when attempting to blit to it.
490 For convenience, it seems to be a good idea to create this symbolic
491 buffer 'window' at the same size as the SDL backbuffer. Although it
492 should never be drawn to directly, it would do no harm nevertheless. */
494 // create additional (symbolic) buffer for double-buffering
495 ReCreateBitmap(&window, video.width, video.height);
497 // create dummy drawing buffer for headless mode, if needed
498 if (program.headless)
499 ReCreateBitmap(&backbuffer, video.width, video.height);
502 void SDLInitVideoBuffer(boolean fullscreen)
504 SDLInitVideoBuffer_VideoBuffer(fullscreen);
505 SDLInitVideoBuffer_DrawBuffer();
508 static boolean SDLCreateScreen(boolean fullscreen)
510 SDL_Surface *new_surface = NULL;
512 int surface_flags_window = SURFACE_FLAGS;
513 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
516 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
518 video.vsync_mode = VSYNC_MODE_OFF;
520 if (!strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF))
522 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
523 video.vsync_mode = VSYNC_MODE_NORMAL;
526 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
527 _without_ enabling 2D/3D acceleration and/or guest additions installed,
528 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
529 it will try to use accelerated graphics and apparently fails miserably) */
530 int renderer_flags = SDL_RENDERER_SOFTWARE;
533 int width = video.width;
534 int height = video.height;
535 int screen_width = video.screen_width;
536 int screen_height = video.screen_height;
537 int surface_flags = (fullscreen ? surface_flags_fullscreen :
538 surface_flags_window);
540 // default window size is unscaled
541 video.window_width = screen_width;
542 video.window_height = screen_height;
544 // store if initial screen mode is fullscreen mode when changing screen size
545 video.fullscreen_initial = fullscreen;
547 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
549 video.window_width = window_scaling_factor * screen_width;
550 video.window_height = window_scaling_factor * screen_height;
552 if (sdl_texture_stream)
554 SDL_DestroyTexture(sdl_texture_stream);
555 sdl_texture_stream = NULL;
558 if (sdl_texture_target)
560 SDL_DestroyTexture(sdl_texture_target);
561 sdl_texture_target = NULL;
564 if (!(fullscreen && fullscreen_enabled))
568 SDL_DestroyRenderer(sdl_renderer);
574 SDL_DestroyWindow(sdl_window);
579 if (sdl_window == NULL)
580 sdl_window = SDL_CreateWindow(program.window_title,
581 SDL_WINDOWPOS_CENTERED,
582 SDL_WINDOWPOS_CENTERED,
587 if (sdl_window != NULL)
589 if (sdl_renderer == NULL)
590 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
592 if (sdl_renderer != NULL)
594 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
595 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
597 // required for setting adaptive vsync when using OpenGL renderer
598 SDLSetScreenVsyncMode(setup.vsync_mode);
600 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
601 SDL_PIXELFORMAT_ARGB8888,
602 SDL_TEXTUREACCESS_STREAMING,
605 if (SDL_RenderTargetSupported(sdl_renderer))
606 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
607 SDL_PIXELFORMAT_ARGB8888,
608 SDL_TEXTUREACCESS_TARGET,
611 if (sdl_texture_stream != NULL)
613 // use SDL default values for RGB masks and no alpha channel
614 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
616 if (new_surface == NULL)
617 Warn("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
621 Warn("SDL_CreateTexture() failed: %s", SDL_GetError());
626 Warn("SDL_CreateRenderer() failed: %s", SDL_GetError());
631 Warn("SDL_CreateWindow() failed: %s", SDL_GetError());
634 SDLSetScreenProperties();
636 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
637 if (new_surface != NULL)
638 fullscreen_enabled = fullscreen;
640 if (backbuffer == NULL)
641 backbuffer = CreateBitmapStruct();
643 backbuffer->width = video.width;
644 backbuffer->height = video.height;
646 if (backbuffer->surface)
647 SDL_FreeSurface(backbuffer->surface);
649 backbuffer->surface = new_surface;
651 return (new_surface != NULL);
654 boolean SDLSetVideoMode(boolean fullscreen)
656 boolean success = FALSE;
660 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
662 // switch display to fullscreen mode, if available
663 success = SDLCreateScreen(TRUE);
667 // switching display to fullscreen mode failed -- do not try it again
668 video.fullscreen_available = FALSE;
672 video.fullscreen_enabled = TRUE;
676 if ((!fullscreen && video.fullscreen_enabled) || !success)
678 // switch display to window mode
679 success = SDLCreateScreen(FALSE);
683 // switching display to window mode failed -- should not happen
687 video.fullscreen_enabled = FALSE;
688 video.window_scaling_percent = setup.window_scaling_percent;
689 video.window_scaling_quality = setup.window_scaling_quality;
691 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
695 SDLRedrawWindow(); // map window
700 void SDLSetWindowTitle(void)
702 if (sdl_window == NULL)
705 SDL_SetWindowTitle(sdl_window, program.window_title);
708 void SDLSetWindowScaling(int window_scaling_percent)
710 if (sdl_window == NULL)
713 float window_scaling_factor = (float)window_scaling_percent / 100;
714 int new_window_width = (int)(window_scaling_factor * video.screen_width);
715 int new_window_height = (int)(window_scaling_factor * video.screen_height);
717 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
719 video.window_scaling_percent = window_scaling_percent;
720 video.window_width = new_window_width;
721 video.window_height = new_window_height;
726 void SDLSetWindowScalingQuality(char *window_scaling_quality)
728 SDL_Texture *new_texture;
730 if (sdl_texture_stream == NULL)
733 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
735 new_texture = SDL_CreateTexture(sdl_renderer,
736 SDL_PIXELFORMAT_ARGB8888,
737 SDL_TEXTUREACCESS_STREAMING,
738 video.width, video.height);
740 if (new_texture != NULL)
742 SDL_DestroyTexture(sdl_texture_stream);
744 sdl_texture_stream = new_texture;
747 if (SDL_RenderTargetSupported(sdl_renderer))
748 new_texture = SDL_CreateTexture(sdl_renderer,
749 SDL_PIXELFORMAT_ARGB8888,
750 SDL_TEXTUREACCESS_TARGET,
751 video.width, video.height);
755 if (new_texture != NULL)
757 SDL_DestroyTexture(sdl_texture_target);
759 sdl_texture_target = new_texture;
764 video.window_scaling_quality = window_scaling_quality;
767 void SDLSetWindowFullscreen(boolean fullscreen)
769 if (sdl_window == NULL)
772 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
774 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
775 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
777 // if screen size was changed in fullscreen mode, correct desktop window size
778 if (!fullscreen && video.fullscreen_initial)
780 SDLSetWindowScaling(setup.window_scaling_percent);
781 SDL_SetWindowPosition(sdl_window,
782 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
784 video.fullscreen_initial = FALSE;
788 void SDLSetDisplaySize(void)
790 if (sdl_renderer != NULL)
794 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
796 video.display_width = w;
797 video.display_height = h;
800 Debug("video", "SDL renderer size: %d x %d",
801 video.display_width, video.display_height);
806 SDL_Rect display_bounds;
808 SDL_GetDisplayBounds(0, &display_bounds);
810 video.display_width = display_bounds.w;
811 video.display_height = display_bounds.h;
814 Debug("video", "SDL display size: %d x %d",
815 video.display_width, video.display_height);
820 void SDLSetScreenSizeAndOffsets(int width, int height)
822 // set default video screen size and offsets
823 video.screen_width = width;
824 video.screen_height = height;
825 video.screen_xoffset = 0;
826 video.screen_yoffset = 0;
828 #if defined(USE_COMPLETE_DISPLAY)
829 float ratio_video = (float) width / height;
830 float ratio_display = (float) video.display_width / video.display_height;
832 if (ratio_video != ratio_display)
834 // adjust drawable screen size to cover the whole device display
836 if (ratio_video < ratio_display)
837 video.screen_width *= ratio_display / ratio_video;
839 video.screen_height *= ratio_video / ratio_display;
841 video.screen_xoffset = (video.screen_width - width) / 2;
842 video.screen_yoffset = (video.screen_height - height) / 2;
845 Debug("video", "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
847 video.screen_width, video.screen_height,
848 ratio_video, ratio_display);
854 void SDLSetScreenSizeForRenderer(int width, int height)
856 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
859 void SDLSetScreenProperties(void)
862 SDLSetScreenSizeAndOffsets(video.width, video.height);
863 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
866 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
868 video.screen_rendering_mode =
869 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
870 SPECIAL_RENDERING_BITMAP :
871 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
872 SPECIAL_RENDERING_TARGET:
873 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
874 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
877 void SDLSetScreenVsyncMode(char *vsync_mode)
879 // changing vsync mode without re-creating renderer only supported by OpenGL
880 if (!strPrefixLower((char *)SDLGetRendererName(), "opengl"))
883 int interval = VSYNC_MODE_STR_TO_INT(vsync_mode);
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 Fail("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 Fail("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 Fail("IMG_Load('%s') failed: %s", getBaseNamePtr(filename), SDL_GetError());
2308 print_timestamp_time("IMG_Load");
2310 UPDATE_BUSY_STATE();
2312 // create native non-transparent surface for current image
2313 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2314 Fail("SDLGetOpaqueSurface() failed");
2316 print_timestamp_time("SDLGetNativeSurface (opaque)");
2318 UPDATE_BUSY_STATE();
2320 // set black pixel to transparent if no alpha channel / transparent color
2321 if (!SDLHasAlpha(sdl_image_tmp) &&
2322 !SDLHasColorKey(sdl_image_tmp))
2323 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2324 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2326 // create native transparent surface for current image
2327 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2328 Fail("SDLGetNativeSurface() failed");
2330 print_timestamp_time("SDLGetNativeSurface (masked)");
2332 UPDATE_BUSY_STATE();
2334 // free temporary surface
2335 SDL_FreeSurface(sdl_image_tmp);
2337 new_bitmap->width = new_bitmap->surface->w;
2338 new_bitmap->height = new_bitmap->surface->h;
2340 print_timestamp_done("SDLLoadImage");
2346 // ----------------------------------------------------------------------------
2347 // custom cursor fuctions
2348 // ----------------------------------------------------------------------------
2350 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2352 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2353 cursor_info->width, cursor_info->height,
2354 cursor_info->hot_x, cursor_info->hot_y);
2357 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2359 static struct MouseCursorInfo *last_cursor_info = NULL;
2360 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2361 static SDL_Cursor *cursor_default = NULL;
2362 static SDL_Cursor *cursor_current = NULL;
2364 // if invoked for the first time, store the SDL default cursor
2365 if (cursor_default == NULL)
2366 cursor_default = SDL_GetCursor();
2368 // only create new cursor if cursor info (custom only) has changed
2369 if (cursor_info != NULL && cursor_info != last_cursor_info)
2371 cursor_current = create_cursor(cursor_info);
2372 last_cursor_info = cursor_info;
2375 // only set new cursor if cursor info (custom or NULL) has changed
2376 if (cursor_info != last_cursor_info2)
2377 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2379 last_cursor_info2 = cursor_info;
2383 // ============================================================================
2385 // ============================================================================
2387 void SDLOpenAudio(void)
2389 if (program.headless)
2392 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2394 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 Warn("Mix_OpenAudio() failed: %s", SDL_GetError());
2408 audio.sound_available = TRUE;
2409 audio.music_available = TRUE;
2410 audio.loops_available = TRUE;
2411 audio.sound_enabled = TRUE;
2413 // set number of available mixer channels
2414 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2415 audio.music_channel = MUSIC_CHANNEL;
2416 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2418 Mixer_InitChannels();
2421 void SDLCloseAudio(void)
2424 Mix_HaltChannel(-1);
2427 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2431 // ============================================================================
2433 // ============================================================================
2435 void SDLWaitEvent(Event *event)
2437 SDL_WaitEvent(event);
2440 void SDLCorrectRawMousePosition(int *x, int *y)
2442 if (sdl_renderer == NULL)
2445 // this corrects the raw mouse position for logical screen size within event
2446 // filters (correction done later by SDL library when handling mouse events)
2449 float scale_x, scale_y;
2451 SDL_RenderGetViewport(sdl_renderer, &viewport);
2452 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2454 *x = (int)(*x / scale_x);
2455 *y = (int)(*y / scale_y);
2462 // ============================================================================
2463 // joystick functions
2464 // ============================================================================
2466 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2467 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2468 static int sdl_js_axis[MAX_PLAYERS][2];
2469 static int sdl_js_button[MAX_PLAYERS][2];
2470 static boolean sdl_is_controller[MAX_PLAYERS];
2472 void SDLClearJoystickState(void)
2476 for (i = 0; i < MAX_PLAYERS; i++)
2478 for (j = 0; j < 2; j++)
2480 sdl_js_axis_raw[i][j] = -1;
2481 sdl_js_axis[i][j] = 0;
2482 sdl_js_button[i][j] = 0;
2487 boolean SDLOpenJoystick(int nr)
2489 if (nr < 0 || nr >= MAX_PLAYERS)
2492 sdl_is_controller[nr] = SDL_IsGameController(nr);
2495 Debug("joystick", "opening joystick %d (%s)",
2496 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2499 if (sdl_is_controller[nr])
2500 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2502 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2504 return (sdl_joystick[nr] != NULL);
2507 void SDLCloseJoystick(int nr)
2509 if (nr < 0 || nr >= MAX_PLAYERS)
2513 Debug("joystick", "closing joystick %d", nr);
2516 if (sdl_is_controller[nr])
2517 SDL_GameControllerClose(sdl_joystick[nr]);
2519 SDL_JoystickClose(sdl_joystick[nr]);
2521 sdl_joystick[nr] = NULL;
2524 boolean SDLCheckJoystickOpened(int nr)
2526 if (nr < 0 || nr >= MAX_PLAYERS)
2529 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2532 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2534 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2535 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2536 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2537 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2539 if (nr < 0 || nr >= MAX_PLAYERS)
2545 // prevent (slightly jittering, but centered) axis A from resetting axis B
2546 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2547 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2550 sdl_js_axis[nr][axis_id] = axis_value;
2551 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2554 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2556 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2557 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2558 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2559 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2560 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2561 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2562 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2563 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2566 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2567 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2568 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2569 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2570 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2571 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2572 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2573 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2575 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2576 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2577 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2578 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2579 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2581 if (nr < 0 || nr >= MAX_PLAYERS)
2584 if (button_id == -1)
2587 sdl_js_button[nr][button_id] = button_state;
2590 void HandleJoystickEvent(Event *event)
2592 // when using joystick, disable overlay touch buttons
2593 runtime.uses_touch_device = FALSE;
2595 switch (event->type)
2597 case SDL_CONTROLLERDEVICEADDED:
2599 Debug("joystick", "SDL_CONTROLLERDEVICEADDED: device %d added",
2600 event->cdevice.which);
2605 case SDL_CONTROLLERDEVICEREMOVED:
2607 Debug("joystick", "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2608 event->cdevice.which);
2613 case SDL_CONTROLLERAXISMOTION:
2615 Debug("joystick", "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2616 event->caxis.which, event->caxis.axis, event->caxis.value);
2618 setJoystickAxis(event->caxis.which,
2620 event->caxis.value);
2623 case SDL_CONTROLLERBUTTONDOWN:
2625 Debug("joystick", "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2626 event->cbutton.which, event->cbutton.button);
2628 setJoystickButton(event->cbutton.which,
2629 event->cbutton.button,
2633 case SDL_CONTROLLERBUTTONUP:
2635 Debug("joystick", "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2636 event->cbutton.which, event->cbutton.button);
2638 setJoystickButton(event->cbutton.which,
2639 event->cbutton.button,
2643 case SDL_JOYAXISMOTION:
2644 if (sdl_is_controller[event->jaxis.which])
2648 Debug("joystick", "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2649 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2651 if (event->jaxis.axis < 4)
2652 setJoystickAxis(event->jaxis.which,
2654 event->jaxis.value);
2657 case SDL_JOYBUTTONDOWN:
2658 if (sdl_is_controller[event->jaxis.which])
2662 Debug("joystick", "SDL_JOYBUTTONDOWN: device %d, button %d",
2663 event->jbutton.which, event->jbutton.button);
2665 if (event->jbutton.button < 4)
2666 setJoystickButton(event->jbutton.which,
2667 event->jbutton.button,
2671 case SDL_JOYBUTTONUP:
2672 if (sdl_is_controller[event->jaxis.which])
2676 Debug("joystick", "SDL_JOYBUTTONUP: device %d, button %d",
2677 event->jbutton.which, event->jbutton.button);
2679 if (event->jbutton.button < 4)
2680 setJoystickButton(event->jbutton.which,
2681 event->jbutton.button,
2690 void SDLInitJoysticks(void)
2692 static boolean sdl_joystick_subsystem_initialized = FALSE;
2693 boolean print_warning = !sdl_joystick_subsystem_initialized;
2694 char *mappings_file_base = getPath2(options.conf_directory,
2695 GAMECONTROLLER_BASENAME);
2696 char *mappings_file_user = getPath2(getUserGameDataDir(),
2697 GAMECONTROLLER_BASENAME);
2701 if (!sdl_joystick_subsystem_initialized)
2703 sdl_joystick_subsystem_initialized = TRUE;
2705 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2707 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2708 Fail("SDL_Init() failed: %s", SDL_GetError());
2710 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2712 // the included game controller base mappings should always be found
2713 if (num_mappings == -1)
2714 Warn("no game controller base mappings found");
2717 Debug("joystick", "%d game controller base mapping(s) added",
2721 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2724 // the personal game controller user mappings may or may not be found
2725 if (num_mappings == -1)
2726 Warn("no game controller user mappings found");
2728 Debug("joystick", , "%d game controller user mapping(s) added",
2731 Debug("joystick", "%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 Debug("joystick", "- 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 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 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();