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 void SDLCopyColorKey(SDL_Surface *src_surface, SDL_Surface *dst_surface)
305 // check if source surface has a color key
306 if (SDL_GetColorKey(src_surface, &color_key) == 0)
308 // get RGB values of color key of source surface
309 SDL_GetRGB(color_key, src_surface->format, &r, &g, &b);
311 // get color key from RGB values in destination surface format
312 color_key = SDL_MapRGB(dst_surface->format, r, g, b);
314 // set color key in destination surface
315 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL, color_key);
319 // unset color key in destination surface
320 SDL_SetColorKey(dst_surface, UNSET_TRANSPARENT_PIXEL, 0);
324 static boolean SDLHasColorKey(SDL_Surface *surface)
328 return (SDL_GetColorKey(surface, &color_key) == 0);
331 static boolean SDLHasAlpha(SDL_Surface *surface)
333 SDL_BlendMode blend_mode;
335 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
338 return (blend_mode == SDL_BLENDMODE_BLEND);
341 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
343 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
345 SDL_SetSurfaceBlendMode(surface, blend_mode);
346 SDL_SetSurfaceAlphaMod(surface, alpha);
349 const char *SDLGetRendererName(void)
351 static SDL_RendererInfo renderer_info;
353 SDL_GetRendererInfo(sdl_renderer, &renderer_info);
355 return renderer_info.name;
358 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
360 SDL_PixelFormat format;
361 SDL_Surface *new_surface;
366 if (backbuffer && backbuffer->surface)
368 format = *backbuffer->surface->format;
369 format.Amask = surface->format->Amask; // keep alpha channel
373 format = *surface->format;
376 new_surface = SDL_ConvertSurface(surface, &format, 0);
378 if (new_surface == NULL)
379 Fail("SDL_ConvertSurface() failed: %s", SDL_GetError());
381 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
382 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
383 SDLCopyColorKey(surface, new_surface);
388 boolean SDLSetNativeSurface(SDL_Surface **surface)
390 SDL_Surface *new_surface;
392 if (surface == NULL ||
394 backbuffer == NULL ||
395 backbuffer->surface == NULL)
398 // if pixel format already optimized for destination surface, do nothing
399 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
402 new_surface = SDLGetNativeSurface(*surface);
404 SDL_FreeSurface(*surface);
406 *surface = new_surface;
411 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
413 if (program.headless)
416 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
419 Fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
424 void SDLCreateBitmapTextures(Bitmap *bitmap)
430 SDL_DestroyTexture(bitmap->texture);
431 if (bitmap->texture_masked)
432 SDL_DestroyTexture(bitmap->texture_masked);
434 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
435 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
438 void SDLFreeBitmapTextures(Bitmap *bitmap)
444 SDL_DestroyTexture(bitmap->texture);
445 if (bitmap->texture_masked)
446 SDL_DestroyTexture(bitmap->texture_masked);
448 bitmap->texture = NULL;
449 bitmap->texture_masked = NULL;
452 void SDLInitVideoDisplay(void)
454 // set hint to select render driver as specified in setup config file
455 if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
456 SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
458 // initialize SDL video
459 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
460 Fail("SDL_InitSubSystem() failed: %s", SDL_GetError());
462 // set default SDL depth
463 video.default_depth = 32; // (how to determine video depth in SDL2?)
465 // Code used with SDL 1.2:
466 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
469 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
471 if (program.headless)
474 video.window_scaling_percent = setup.window_scaling_percent;
475 video.window_scaling_quality = setup.window_scaling_quality;
477 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
479 // SDL 2.0: support for (desktop) fullscreen mode available
480 video.fullscreen_available = TRUE;
482 // open SDL video output device (window or fullscreen mode)
483 if (!SDLSetVideoMode(fullscreen))
484 Fail("setting video mode failed");
486 // !!! SDL2 can only set the window icon if the window already exists !!!
488 SDLSetWindowIcon(program.icon_filename);
490 // set window and icon title
494 static void SDLInitVideoBuffer_DrawBuffer(void)
496 /* SDL cannot directly draw to the visible video framebuffer like X11,
497 but always uses a backbuffer, which is then blitted to the visible
498 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
499 visible video framebuffer with 'SDL_Flip', if the hardware supports
500 this). Therefore do not use an additional backbuffer for drawing, but
501 use a symbolic buffer (distinguishable from the SDL backbuffer) called
502 'window', which indicates that the SDL backbuffer should be updated to
503 the visible video framebuffer when attempting to blit to it.
505 For convenience, it seems to be a good idea to create this symbolic
506 buffer 'window' at the same size as the SDL backbuffer. Although it
507 should never be drawn to directly, it would do no harm nevertheless. */
509 // create additional (symbolic) buffer for double-buffering
510 ReCreateBitmap(&window, video.width, video.height);
512 // create dummy drawing buffer for headless mode, if needed
513 if (program.headless)
514 ReCreateBitmap(&backbuffer, video.width, video.height);
517 void SDLInitVideoBuffer(boolean fullscreen)
519 SDLInitVideoBuffer_VideoBuffer(fullscreen);
520 SDLInitVideoBuffer_DrawBuffer();
523 static boolean SDLCreateScreen(boolean fullscreen)
525 SDL_Surface *new_surface = NULL;
527 int surface_flags_window = SURFACE_FLAGS;
528 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
531 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
533 video.vsync_mode = VSYNC_MODE_OFF;
535 if (!strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF))
537 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
538 video.vsync_mode = VSYNC_MODE_NORMAL;
541 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
542 _without_ enabling 2D/3D acceleration and/or guest additions installed,
543 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
544 it will try to use accelerated graphics and apparently fails miserably) */
545 int renderer_flags = SDL_RENDERER_SOFTWARE;
548 int width = video.width;
549 int height = video.height;
550 int screen_width = video.screen_width;
551 int screen_height = video.screen_height;
552 int surface_flags = (fullscreen ? surface_flags_fullscreen :
553 surface_flags_window);
555 // default window size is unscaled
556 video.window_width = screen_width;
557 video.window_height = screen_height;
559 // store if initial screen mode is fullscreen mode when changing screen size
560 video.fullscreen_initial = fullscreen;
562 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
564 video.window_width = window_scaling_factor * screen_width;
565 video.window_height = window_scaling_factor * screen_height;
567 if (sdl_texture_stream)
569 SDL_DestroyTexture(sdl_texture_stream);
570 sdl_texture_stream = NULL;
573 if (sdl_texture_target)
575 SDL_DestroyTexture(sdl_texture_target);
576 sdl_texture_target = NULL;
579 if (!(fullscreen && fullscreen_enabled))
583 SDL_DestroyRenderer(sdl_renderer);
589 SDL_DestroyWindow(sdl_window);
594 if (sdl_window == NULL)
595 sdl_window = SDL_CreateWindow(program.window_title,
596 SDL_WINDOWPOS_CENTERED,
597 SDL_WINDOWPOS_CENTERED,
602 if (sdl_window != NULL)
604 if (sdl_renderer == NULL)
605 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
607 if (sdl_renderer != NULL)
609 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
610 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
612 // required for setting adaptive vsync when using OpenGL renderer
613 SDLSetScreenVsyncMode(setup.vsync_mode);
615 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
616 SDL_PIXELFORMAT_ARGB8888,
617 SDL_TEXTUREACCESS_STREAMING,
620 if (SDL_RenderTargetSupported(sdl_renderer))
621 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
622 SDL_PIXELFORMAT_ARGB8888,
623 SDL_TEXTUREACCESS_TARGET,
626 if (sdl_texture_stream != NULL)
628 // use SDL default values for RGB masks and no alpha channel
629 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
631 if (new_surface == NULL)
632 Warn("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
636 Warn("SDL_CreateTexture() failed: %s", SDL_GetError());
641 Warn("SDL_CreateRenderer() failed: %s", SDL_GetError());
646 Warn("SDL_CreateWindow() failed: %s", SDL_GetError());
649 SDLSetScreenProperties();
651 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
652 if (new_surface != NULL)
653 fullscreen_enabled = fullscreen;
655 if (backbuffer == NULL)
656 backbuffer = CreateBitmapStruct();
658 backbuffer->width = video.width;
659 backbuffer->height = video.height;
661 if (backbuffer->surface)
662 SDL_FreeSurface(backbuffer->surface);
664 backbuffer->surface = new_surface;
666 return (new_surface != NULL);
669 boolean SDLSetVideoMode(boolean fullscreen)
671 boolean success = FALSE;
675 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
677 // switch display to fullscreen mode, if available
678 success = SDLCreateScreen(TRUE);
682 // switching display to fullscreen mode failed -- do not try it again
683 video.fullscreen_available = FALSE;
687 video.fullscreen_enabled = TRUE;
691 if ((!fullscreen && video.fullscreen_enabled) || !success)
693 // switch display to window mode
694 success = SDLCreateScreen(FALSE);
698 // switching display to window mode failed -- should not happen
702 video.fullscreen_enabled = FALSE;
703 video.window_scaling_percent = setup.window_scaling_percent;
704 video.window_scaling_quality = setup.window_scaling_quality;
706 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
710 SDLRedrawWindow(); // map window
715 void SDLSetWindowTitle(void)
717 if (sdl_window == NULL)
720 SDL_SetWindowTitle(sdl_window, program.window_title);
723 void SDLSetWindowScaling(int window_scaling_percent)
725 if (sdl_window == NULL)
728 float window_scaling_factor = (float)window_scaling_percent / 100;
729 int new_window_width = (int)(window_scaling_factor * video.screen_width);
730 int new_window_height = (int)(window_scaling_factor * video.screen_height);
732 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
734 video.window_scaling_percent = window_scaling_percent;
735 video.window_width = new_window_width;
736 video.window_height = new_window_height;
741 void SDLSetWindowScalingQuality(char *window_scaling_quality)
743 SDL_Texture *new_texture;
745 if (sdl_texture_stream == NULL)
748 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
750 new_texture = SDL_CreateTexture(sdl_renderer,
751 SDL_PIXELFORMAT_ARGB8888,
752 SDL_TEXTUREACCESS_STREAMING,
753 video.width, video.height);
755 if (new_texture != NULL)
757 SDL_DestroyTexture(sdl_texture_stream);
759 sdl_texture_stream = new_texture;
762 if (SDL_RenderTargetSupported(sdl_renderer))
763 new_texture = SDL_CreateTexture(sdl_renderer,
764 SDL_PIXELFORMAT_ARGB8888,
765 SDL_TEXTUREACCESS_TARGET,
766 video.width, video.height);
770 if (new_texture != NULL)
772 SDL_DestroyTexture(sdl_texture_target);
774 sdl_texture_target = new_texture;
779 video.window_scaling_quality = window_scaling_quality;
782 void SDLSetWindowFullscreen(boolean fullscreen)
784 if (sdl_window == NULL)
787 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
789 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
790 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
792 // if screen size was changed in fullscreen mode, correct desktop window size
793 if (!fullscreen && video.fullscreen_initial)
795 SDLSetWindowScaling(setup.window_scaling_percent);
796 SDL_SetWindowPosition(sdl_window,
797 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
799 video.fullscreen_initial = FALSE;
803 void SDLSetDisplaySize(void)
805 if (sdl_renderer != NULL)
809 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
811 video.display_width = w;
812 video.display_height = h;
815 Debug("video", "SDL renderer size: %d x %d",
816 video.display_width, video.display_height);
821 SDL_Rect display_bounds;
823 SDL_GetDisplayBounds(0, &display_bounds);
825 video.display_width = display_bounds.w;
826 video.display_height = display_bounds.h;
829 Debug("video", "SDL display size: %d x %d",
830 video.display_width, video.display_height);
835 void SDLSetScreenSizeAndOffsets(int width, int height)
837 // set default video screen size and offsets
838 video.screen_width = width;
839 video.screen_height = height;
840 video.screen_xoffset = 0;
841 video.screen_yoffset = 0;
843 #if defined(USE_COMPLETE_DISPLAY)
844 float ratio_video = (float) width / height;
845 float ratio_display = (float) video.display_width / video.display_height;
847 if (ratio_video != ratio_display)
849 // adjust drawable screen size to cover the whole device display
851 if (ratio_video < ratio_display)
852 video.screen_width *= ratio_display / ratio_video;
854 video.screen_height *= ratio_video / ratio_display;
856 video.screen_xoffset = (video.screen_width - width) / 2;
857 video.screen_yoffset = (video.screen_height - height) / 2;
860 Debug("video", "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
862 video.screen_width, video.screen_height,
863 ratio_video, ratio_display);
869 void SDLSetScreenSizeForRenderer(int width, int height)
871 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
874 void SDLSetScreenProperties(void)
877 SDLSetScreenSizeAndOffsets(video.width, video.height);
878 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
881 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
883 video.screen_rendering_mode =
884 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
885 SPECIAL_RENDERING_BITMAP :
886 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
887 SPECIAL_RENDERING_TARGET:
888 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
889 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
892 void SDLSetScreenVsyncMode(char *vsync_mode)
894 // changing vsync mode without re-creating renderer only supported by OpenGL
895 if (!strPrefixLower((char *)SDLGetRendererName(), "opengl"))
898 int interval = VSYNC_MODE_STR_TO_INT(vsync_mode);
899 int result = SDL_GL_SetSwapInterval(interval);
901 // if adaptive vsync requested, but not supported, retry with normal vsync
902 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
904 interval = VSYNC_MODE_NORMAL;
906 result = SDL_GL_SetSwapInterval(interval);
910 interval = VSYNC_MODE_OFF;
912 video.vsync_mode = interval;
915 void SDLRedrawWindow(void)
917 UpdateScreen_WithoutFrameDelay(NULL);
920 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
923 if (program.headless)
926 SDL_Surface *surface =
927 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
930 Fail("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
932 SDLSetNativeSurface(&surface);
934 bitmap->surface = surface;
937 void SDLFreeBitmapPointers(Bitmap *bitmap)
940 SDL_FreeSurface(bitmap->surface);
941 if (bitmap->surface_masked)
942 SDL_FreeSurface(bitmap->surface_masked);
944 bitmap->surface = NULL;
945 bitmap->surface_masked = NULL;
948 SDL_DestroyTexture(bitmap->texture);
949 if (bitmap->texture_masked)
950 SDL_DestroyTexture(bitmap->texture_masked);
952 bitmap->texture = NULL;
953 bitmap->texture_masked = NULL;
956 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
957 int src_x, int src_y, int width, int height,
958 int dst_x, int dst_y, int mask_mode)
960 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
961 SDL_Rect src_rect, dst_rect;
973 // if (src_bitmap != backbuffer || dst_bitmap != window)
974 if (!(src_bitmap == backbuffer && dst_bitmap == window))
975 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
976 src_bitmap->surface_masked : src_bitmap->surface),
977 &src_rect, real_dst_bitmap->surface, &dst_rect);
979 if (dst_bitmap == window)
980 UpdateScreen_WithFrameDelay(&dst_rect);
983 void SDLBlitTexture(Bitmap *bitmap,
984 int src_x, int src_y, int width, int height,
985 int dst_x, int dst_y, int mask_mode)
987 SDL_Texture *texture;
992 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1000 src_rect.h = height;
1005 dst_rect.h = height;
1007 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1010 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1013 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1021 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1023 if (dst_bitmap == window)
1024 UpdateScreen_WithFrameDelay(&rect);
1027 void PrepareFadeBitmap(int draw_target)
1029 Bitmap *fade_bitmap =
1030 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1031 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1033 if (fade_bitmap == NULL)
1036 // copy backbuffer to fading buffer
1037 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1039 // add border and animations to fading buffer
1040 FinalizeScreen(draw_target);
1043 void SDLFadeRectangle(int x, int y, int width, int height,
1044 int fade_mode, int fade_delay, int post_delay,
1045 void (*draw_border_function)(void))
1047 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1048 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1049 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1050 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1051 SDL_Surface *surface_screen = backbuffer->surface;
1052 SDL_Rect src_rect, dst_rect;
1054 int src_x = x, src_y = y;
1055 int dst_x = x, dst_y = y;
1056 unsigned int time_last, time_current;
1058 // store function for drawing global masked border
1059 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1061 // deactivate drawing of global border while fading, if needed
1062 if (draw_border_function == NULL)
1063 gfx.draw_global_border_function = NULL;
1068 src_rect.h = height;
1072 dst_rect.w = width; // (ignored)
1073 dst_rect.h = height; // (ignored)
1075 dst_rect2 = dst_rect;
1077 // before fading in, store backbuffer (without animation graphics)
1078 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1079 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1081 // copy source and target surfaces to temporary surfaces for fading
1082 if (fade_mode & FADE_TYPE_TRANSFORM)
1084 // (source and target fading buffer already prepared)
1086 else if (fade_mode & FADE_TYPE_FADE_IN)
1088 // (target fading buffer already prepared)
1089 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1091 else // FADE_TYPE_FADE_OUT
1093 // (source fading buffer already prepared)
1094 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1097 time_current = SDL_GetTicks();
1099 if (fade_delay <= 0)
1101 // immediately draw final target frame without delay
1102 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1106 // when fading without delay, also skip post delay
1110 if (fade_mode == FADE_MODE_MELT)
1112 boolean done = FALSE;
1113 int melt_pixels = 2;
1114 int melt_columns = width / melt_pixels;
1115 int ypos[melt_columns];
1116 int max_steps = height / 8 + 32;
1121 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1123 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1125 ypos[0] = -GetSimpleRandom(16);
1127 for (i = 1 ; i < melt_columns; i++)
1129 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1131 ypos[i] = ypos[i - 1] + r;
1144 time_last = time_current;
1145 time_current = SDL_GetTicks();
1146 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1147 steps_final = MIN(MAX(0, steps), max_steps);
1151 done = (steps_done >= steps_final);
1153 for (i = 0 ; i < melt_columns; i++)
1161 else if (ypos[i] < height)
1166 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1168 if (ypos[i] + dy >= height)
1169 dy = height - ypos[i];
1171 // copy part of (appearing) target surface to upper area
1172 src_rect.x = src_x + i * melt_pixels;
1173 // src_rect.y = src_y + ypos[i];
1175 src_rect.w = melt_pixels;
1177 src_rect.h = ypos[i] + dy;
1179 dst_rect.x = dst_x + i * melt_pixels;
1180 // dst_rect.y = dst_y + ypos[i];
1183 if (steps_done >= steps_final)
1184 SDL_BlitSurface(surface_target, &src_rect,
1185 surface_screen, &dst_rect);
1189 // copy part of (disappearing) source surface to lower area
1190 src_rect.x = src_x + i * melt_pixels;
1192 src_rect.w = melt_pixels;
1193 src_rect.h = height - ypos[i];
1195 dst_rect.x = dst_x + i * melt_pixels;
1196 dst_rect.y = dst_y + ypos[i];
1198 if (steps_done >= steps_final)
1199 SDL_BlitSurface(surface_source, &src_rect,
1200 surface_screen, &dst_rect);
1206 src_rect.x = src_x + i * melt_pixels;
1208 src_rect.w = melt_pixels;
1209 src_rect.h = height;
1211 dst_rect.x = dst_x + i * melt_pixels;
1214 if (steps_done >= steps_final)
1215 SDL_BlitSurface(surface_target, &src_rect,
1216 surface_screen, &dst_rect);
1220 if (steps_done >= steps_final)
1222 if (draw_border_function != NULL)
1223 draw_border_function();
1225 UpdateScreen_WithFrameDelay(&dst_rect2);
1229 else if (fade_mode == FADE_MODE_CURTAIN)
1233 int xx_size = width / 2;
1235 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1237 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1239 for (xx = 0; xx < xx_size;)
1241 time_last = time_current;
1242 time_current = SDL_GetTicks();
1243 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1244 xx_final = MIN(MAX(0, xx), xx_size);
1249 src_rect.h = height;
1254 // draw new (target) image to screen buffer
1255 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1257 if (xx_final < xx_size)
1259 src_rect.w = xx_size - xx_final;
1260 src_rect.h = height;
1262 // draw old (source) image to screen buffer (left side)
1264 src_rect.x = src_x + xx_final;
1267 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1269 // draw old (source) image to screen buffer (right side)
1271 src_rect.x = src_x + xx_size;
1272 dst_rect.x = dst_x + xx_size + xx_final;
1274 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1277 if (draw_border_function != NULL)
1278 draw_border_function();
1280 // only update the region of the screen that is affected from fading
1281 UpdateScreen_WithFrameDelay(&dst_rect2);
1284 else // fading in, fading out or cross-fading
1289 for (alpha = 0.0; alpha < 255.0;)
1291 time_last = time_current;
1292 time_current = SDL_GetTicks();
1293 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1294 alpha_final = MIN(MAX(0, alpha), 255);
1296 // draw existing (source) image to screen buffer
1297 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1299 // draw new (target) image to screen buffer using alpha blending
1300 SDLSetAlpha(surface_target, TRUE, alpha_final);
1301 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1303 if (draw_border_function != NULL)
1304 draw_border_function();
1306 // only update the region of the screen that is affected from fading
1307 UpdateScreen_WithFrameDelay(&dst_rect);
1312 Delay_WithScreenUpdates(post_delay);
1314 // restore function for drawing global masked border
1315 gfx.draw_global_border_function = draw_global_border_function;
1317 // after fading in, restore backbuffer (without animation graphics)
1318 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1319 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1322 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1323 int to_x, int to_y, Uint32 color)
1325 SDL_Surface *surface = dst_bitmap->surface;
1329 swap_numbers(&from_x, &to_x);
1332 swap_numbers(&from_y, &to_y);
1336 rect.w = (to_x - from_x + 1);
1337 rect.h = (to_y - from_y + 1);
1339 SDL_FillRect(surface, &rect, color);
1342 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1343 int to_x, int to_y, Uint32 color)
1345 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1348 #if ENABLE_UNUSED_CODE
1349 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1350 int num_points, Uint32 color)
1355 for (i = 0; i < num_points - 1; i++)
1357 for (x = 0; x < line_width; x++)
1359 for (y = 0; y < line_width; y++)
1361 int dx = x - line_width / 2;
1362 int dy = y - line_width / 2;
1364 if ((x == 0 && y == 0) ||
1365 (x == 0 && y == line_width - 1) ||
1366 (x == line_width - 1 && y == 0) ||
1367 (x == line_width - 1 && y == line_width - 1))
1370 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1371 points[i+1].x + dx, points[i+1].y + dy, color);
1378 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1380 SDL_Surface *surface = src_bitmap->surface;
1382 switch (surface->format->BytesPerPixel)
1384 case 1: // assuming 8-bpp
1386 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1390 case 2: // probably 15-bpp or 16-bpp
1392 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1396 case 3: // slow 24-bpp mode; usually not used
1399 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1403 shift = surface->format->Rshift;
1404 color |= *(pix + shift / 8) >> shift;
1405 shift = surface->format->Gshift;
1406 color |= *(pix + shift / 8) >> shift;
1407 shift = surface->format->Bshift;
1408 color |= *(pix + shift / 8) >> shift;
1414 case 4: // probably 32-bpp
1416 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1425 // ============================================================================
1426 // The following functions were taken from the SGE library
1427 // (SDL Graphics Extension Library) by Anders Lindström
1428 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1429 // ============================================================================
1431 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1433 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1435 switch (surface->format->BytesPerPixel)
1440 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1446 // Probably 15-bpp or 16-bpp
1447 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1453 // Slow 24-bpp mode, usually not used
1457 // Gack - slow, but endian correct
1458 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1459 shift = surface->format->Rshift;
1460 *(pix+shift/8) = color>>shift;
1461 shift = surface->format->Gshift;
1462 *(pix+shift/8) = color>>shift;
1463 shift = surface->format->Bshift;
1464 *(pix+shift/8) = color>>shift;
1471 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1479 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1480 Uint8 R, Uint8 G, Uint8 B)
1482 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1485 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1487 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1490 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1492 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1495 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1500 // Gack - slow, but endian correct
1501 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1502 shift = surface->format->Rshift;
1503 *(pix+shift/8) = color>>shift;
1504 shift = surface->format->Gshift;
1505 *(pix+shift/8) = color>>shift;
1506 shift = surface->format->Bshift;
1507 *(pix+shift/8) = color>>shift;
1510 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1512 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1515 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1517 switch (dest->format->BytesPerPixel)
1520 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1524 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1528 _PutPixel24(dest,x,y,color);
1532 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1538 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1540 if (SDL_MUSTLOCK(surface))
1542 if (SDL_LockSurface(surface) < 0)
1548 _PutPixel(surface, x, y, color);
1550 if (SDL_MUSTLOCK(surface))
1552 SDL_UnlockSurface(surface);
1557 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1558 Uint8 r, Uint8 g, Uint8 b)
1560 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1563 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1565 if (y >= 0 && y <= dest->h - 1)
1567 switch (dest->format->BytesPerPixel)
1570 return y*dest->pitch;
1574 return y*dest->pitch/2;
1578 return y*dest->pitch;
1582 return y*dest->pitch/4;
1590 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1593 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1595 switch (surface->format->BytesPerPixel)
1600 *((Uint8 *)surface->pixels + ypitch + x) = color;
1606 // Probably 15-bpp or 16-bpp
1607 *((Uint16 *)surface->pixels + ypitch + x) = color;
1613 // Slow 24-bpp mode, usually not used
1617 // Gack - slow, but endian correct
1618 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1619 shift = surface->format->Rshift;
1620 *(pix+shift/8) = color>>shift;
1621 shift = surface->format->Gshift;
1622 *(pix+shift/8) = color>>shift;
1623 shift = surface->format->Bshift;
1624 *(pix+shift/8) = color>>shift;
1631 *((Uint32 *)surface->pixels + ypitch + x) = color;
1638 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1643 if (SDL_MUSTLOCK(Surface))
1645 if (SDL_LockSurface(Surface) < 0)
1659 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1663 if (x2 > Surface->w - 1)
1664 x2 = Surface->w - 1;
1671 SDL_FillRect(Surface, &l, Color);
1673 if (SDL_MUSTLOCK(Surface))
1675 SDL_UnlockSurface(Surface);
1679 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1680 Uint8 R, Uint8 G, Uint8 B)
1682 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1685 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1698 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1702 if (x2 > Surface->w - 1)
1703 x2 = Surface->w - 1;
1710 SDL_FillRect(Surface, &l, Color);
1713 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1718 if (SDL_MUSTLOCK(Surface))
1720 if (SDL_LockSurface(Surface) < 0)
1734 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1738 if (y2 > Surface->h - 1)
1739 y2 = Surface->h - 1;
1746 SDL_FillRect(Surface, &l, Color);
1748 if (SDL_MUSTLOCK(Surface))
1750 SDL_UnlockSurface(Surface);
1754 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1755 Uint8 R, Uint8 G, Uint8 B)
1757 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1760 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1773 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1777 if (y2 > Surface->h - 1)
1778 y2 = Surface->h - 1;
1785 SDL_FillRect(Surface, &l, Color);
1789 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1790 Sint16 x2, Sint16 y2, Uint32 Color,
1791 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1794 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1799 sdx = (dx < 0) ? -1 : 1;
1800 sdy = (dy < 0) ? -1 : 1;
1812 for (x = 0; x < dx; x++)
1814 Callback(Surface, px, py, Color);
1828 for (y = 0; y < dy; y++)
1830 Callback(Surface, px, py, Color);
1845 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1846 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1847 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1850 sge_DoLine(Surface, X1, Y1, X2, Y2,
1851 SDL_MapRGB(Surface->format, R, G, B), Callback);
1855 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1858 if (SDL_MUSTLOCK(Surface))
1860 if (SDL_LockSurface(Surface) < 0)
1865 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1867 // unlock the display
1868 if (SDL_MUSTLOCK(Surface))
1870 SDL_UnlockSurface(Surface);
1875 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1876 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1878 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1882 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1884 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1888 // ----------------------------------------------------------------------------
1889 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1890 // ----------------------------------------------------------------------------
1892 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1893 int width, int height, Uint32 color)
1897 for (y = src_y; y < src_y + height; y++)
1899 for (x = src_x; x < src_x + width; x++)
1901 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1903 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1908 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1909 int src_x, int src_y, int width, int height,
1910 int dst_x, int dst_y)
1914 for (y = 0; y < height; y++)
1916 for (x = 0; x < width; x++)
1918 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1920 if (pixel != BLACK_PIXEL)
1921 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1927 // ============================================================================
1928 // The following functions were taken from the SDL_gfx library version 2.0.3
1929 // (Rotozoomer) by Andreas Schiffler
1930 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1931 // ============================================================================
1933 // ----------------------------------------------------------------------------
1936 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1937 // ----------------------------------------------------------------------------
1947 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1950 tColorRGBA *sp, *csp, *dp;
1954 sp = csp = (tColorRGBA *) src->pixels;
1955 dp = (tColorRGBA *) dst->pixels;
1956 dgap = dst->pitch - dst->w * 4;
1958 for (y = 0; y < dst->h; y++)
1962 for (x = 0; x < dst->w; x++)
1964 tColorRGBA *sp0 = sp;
1965 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1966 tColorRGBA *sp00 = &sp0[0];
1967 tColorRGBA *sp01 = &sp0[1];
1968 tColorRGBA *sp10 = &sp1[0];
1969 tColorRGBA *sp11 = &sp1[1];
1972 // create new color pixel from all four source color pixels
1973 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1974 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1975 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1976 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1981 // advance source pointers
1984 // advance destination pointer
1988 // advance source pointer
1989 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1991 // advance destination pointers
1992 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1998 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2000 int x, y, *sax, *say, *csax, *csay;
2002 tColorRGBA *sp, *csp, *csp0, *dp;
2005 // use specialized zoom function when scaling down to exactly half size
2006 if (src->w == 2 * dst->w &&
2007 src->h == 2 * dst->h)
2008 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2011 sx = (float) src->w / (float) dst->w;
2012 sy = (float) src->h / (float) dst->h;
2014 // allocate memory for row increments
2015 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2016 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2018 // precalculate row increments
2019 for (x = 0; x <= dst->w; x++)
2020 *csax++ = (int)(sx * x);
2022 for (y = 0; y <= dst->h; y++)
2023 *csay++ = (int)(sy * y);
2026 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2027 dp = (tColorRGBA *) dst->pixels;
2028 dgap = dst->pitch - dst->w * 4;
2031 for (y = 0; y < dst->h; y++)
2036 for (x = 0; x < dst->w; x++)
2041 // advance source pointers
2045 // advance destination pointer
2049 // advance source pointer
2051 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2053 // advance destination pointers
2054 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2063 // ----------------------------------------------------------------------------
2066 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2067 // ----------------------------------------------------------------------------
2069 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2071 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2072 Uint8 *sp, *dp, *csp;
2076 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2077 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2079 // allocate memory for row increments
2080 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2081 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2083 // precalculate row increments
2086 for (x = 0; x < dst->w; x++)
2089 *csax = (csx >> 16);
2096 for (y = 0; y < dst->h; y++)
2099 *csay = (csy >> 16);
2106 for (x = 0; x < dst->w; x++)
2114 for (y = 0; y < dst->h; y++)
2121 sp = csp = (Uint8 *) src->pixels;
2122 dp = (Uint8 *) dst->pixels;
2123 dgap = dst->pitch - dst->w;
2127 for (y = 0; y < dst->h; y++)
2131 for (x = 0; x < dst->w; x++)
2136 // advance source pointers
2140 // advance destination pointer
2144 // advance source pointer (for row)
2145 csp += ((*csay) * src->pitch);
2148 // advance destination pointers
2158 // ----------------------------------------------------------------------------
2161 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2162 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2163 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2164 // into a 32bit RGBA format on the fly.
2165 // ----------------------------------------------------------------------------
2167 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2169 SDL_Surface *zoom_src = NULL;
2170 SDL_Surface *zoom_dst = NULL;
2171 boolean is_converted = FALSE;
2178 // determine if source surface is 32 bit or 8 bit
2179 is_32bit = (src->format->BitsPerPixel == 32);
2181 if (is_32bit || src->format->BitsPerPixel == 8)
2183 // use source surface 'as is'
2188 // new source surface is 32 bit with a defined RGB ordering
2189 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2190 0x000000ff, 0x0000ff00, 0x00ff0000,
2191 (src->format->Amask ? 0xff000000 : 0));
2192 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2194 is_converted = TRUE;
2197 // allocate surface to completely contain the zoomed surface
2200 // target surface is 32 bit with source RGBA/ABGR ordering
2201 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2202 zoom_src->format->Rmask,
2203 zoom_src->format->Gmask,
2204 zoom_src->format->Bmask,
2205 zoom_src->format->Amask);
2209 // target surface is 8 bit
2210 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2214 // lock source surface
2215 SDL_LockSurface(zoom_src);
2217 // check which kind of surface we have
2220 // call the 32 bit transformation routine to do the zooming
2221 zoomSurfaceRGBA(zoom_src, zoom_dst);
2226 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2227 zoom_dst->format->palette->colors[i] =
2228 zoom_src->format->palette->colors[i];
2229 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2231 // call the 8 bit transformation routine to do the zooming
2232 zoomSurfaceY(zoom_src, zoom_dst);
2235 // unlock source surface
2236 SDL_UnlockSurface(zoom_src);
2238 // free temporary surface
2240 SDL_FreeSurface(zoom_src);
2242 // return destination surface
2246 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2248 SDL_Surface *new_surface;
2250 if (surface == NULL)
2253 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2254 Fail("SDLGetNativeSurface() failed");
2256 // remove alpha channel from native non-transparent surface, if defined
2257 SDLSetAlpha(new_surface, FALSE, 0);
2259 // remove transparent color from native non-transparent surface, if defined
2260 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2265 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2267 Bitmap *dst_bitmap = CreateBitmapStruct();
2268 SDL_Surface *src_surface = src_bitmap->surface_masked;
2269 SDL_Surface *dst_surface;
2271 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2272 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2274 dst_bitmap->width = dst_width;
2275 dst_bitmap->height = dst_height;
2277 // create zoomed temporary surface from source surface
2278 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2280 // create native format destination surface from zoomed temporary surface
2281 SDLSetNativeSurface(&dst_surface);
2283 // set color key for zoomed surface from source surface, if defined
2284 if (SDLHasColorKey(src_surface))
2285 SDLCopyColorKey(src_surface, dst_surface);
2287 // create native non-transparent surface for opaque blitting
2288 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2290 // set native transparent surface for masked blitting
2291 dst_bitmap->surface_masked = dst_surface;
2297 // ============================================================================
2298 // load image to bitmap
2299 // ============================================================================
2301 Bitmap *SDLLoadImage(char *filename)
2303 Bitmap *new_bitmap = CreateBitmapStruct();
2304 SDL_Surface *sdl_image_tmp;
2306 if (program.headless)
2308 // prevent sanity check warnings at later stage
2309 new_bitmap->width = new_bitmap->height = 1;
2314 print_timestamp_init("SDLLoadImage");
2316 print_timestamp_time(getBaseNamePtr(filename));
2318 // load image to temporary surface
2319 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2320 Fail("IMG_Load('%s') failed: %s", getBaseNamePtr(filename), SDL_GetError());
2322 print_timestamp_time("IMG_Load");
2324 UPDATE_BUSY_STATE();
2326 // create native non-transparent surface for current image
2327 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2328 Fail("SDLGetOpaqueSurface() failed");
2330 print_timestamp_time("SDLGetNativeSurface (opaque)");
2332 UPDATE_BUSY_STATE();
2334 // set black pixel to transparent if no alpha channel / transparent color
2335 if (!SDLHasAlpha(sdl_image_tmp) &&
2336 !SDLHasColorKey(sdl_image_tmp))
2337 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2338 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2340 // create native transparent surface for current image
2341 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2342 Fail("SDLGetNativeSurface() failed");
2344 print_timestamp_time("SDLGetNativeSurface (masked)");
2346 UPDATE_BUSY_STATE();
2348 // free temporary surface
2349 SDL_FreeSurface(sdl_image_tmp);
2351 new_bitmap->width = new_bitmap->surface->w;
2352 new_bitmap->height = new_bitmap->surface->h;
2354 print_timestamp_done("SDLLoadImage");
2360 // ----------------------------------------------------------------------------
2361 // custom cursor fuctions
2362 // ----------------------------------------------------------------------------
2364 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2366 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2367 cursor_info->width, cursor_info->height,
2368 cursor_info->hot_x, cursor_info->hot_y);
2371 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2373 static struct MouseCursorInfo *last_cursor_info = NULL;
2374 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2375 static SDL_Cursor *cursor_default = NULL;
2376 static SDL_Cursor *cursor_current = NULL;
2378 // if invoked for the first time, store the SDL default cursor
2379 if (cursor_default == NULL)
2380 cursor_default = SDL_GetCursor();
2382 // only create new cursor if cursor info (custom only) has changed
2383 if (cursor_info != NULL && cursor_info != last_cursor_info)
2385 cursor_current = create_cursor(cursor_info);
2386 last_cursor_info = cursor_info;
2389 // only set new cursor if cursor info (custom or NULL) has changed
2390 if (cursor_info != last_cursor_info2)
2391 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2393 last_cursor_info2 = cursor_info;
2397 // ============================================================================
2399 // ============================================================================
2401 void SDLOpenAudio(void)
2403 if (program.headless)
2406 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2408 Warn("SDL_InitSubSystem() failed: %s", SDL_GetError());
2413 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2414 AUDIO_NUM_CHANNELS_STEREO,
2415 setup.system.audio_fragment_size) < 0)
2417 Warn("Mix_OpenAudio() failed: %s", SDL_GetError());
2422 audio.sound_available = TRUE;
2423 audio.music_available = TRUE;
2424 audio.loops_available = TRUE;
2425 audio.sound_enabled = TRUE;
2427 // set number of available mixer channels
2428 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2429 audio.music_channel = MUSIC_CHANNEL;
2430 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2432 Mixer_InitChannels();
2435 void SDLCloseAudio(void)
2438 Mix_HaltChannel(-1);
2441 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2445 // ============================================================================
2447 // ============================================================================
2449 void SDLWaitEvent(Event *event)
2451 SDL_WaitEvent(event);
2454 void SDLCorrectRawMousePosition(int *x, int *y)
2456 if (sdl_renderer == NULL)
2459 // this corrects the raw mouse position for logical screen size within event
2460 // filters (correction done later by SDL library when handling mouse events)
2463 float scale_x, scale_y;
2465 SDL_RenderGetViewport(sdl_renderer, &viewport);
2466 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2468 *x = (int)(*x / scale_x);
2469 *y = (int)(*y / scale_y);
2476 // ============================================================================
2477 // joystick functions
2478 // ============================================================================
2480 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2481 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2482 static int sdl_js_axis[MAX_PLAYERS][2];
2483 static int sdl_js_button[MAX_PLAYERS][2];
2484 static boolean sdl_is_controller[MAX_PLAYERS];
2486 void SDLClearJoystickState(void)
2490 for (i = 0; i < MAX_PLAYERS; i++)
2492 for (j = 0; j < 2; j++)
2494 sdl_js_axis_raw[i][j] = -1;
2495 sdl_js_axis[i][j] = 0;
2496 sdl_js_button[i][j] = 0;
2501 boolean SDLOpenJoystick(int nr)
2503 if (nr < 0 || nr >= MAX_PLAYERS)
2506 sdl_is_controller[nr] = SDL_IsGameController(nr);
2509 Debug("joystick", "opening joystick %d (%s)",
2510 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2513 if (sdl_is_controller[nr])
2514 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2516 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2518 return (sdl_joystick[nr] != NULL);
2521 void SDLCloseJoystick(int nr)
2523 if (nr < 0 || nr >= MAX_PLAYERS)
2527 Debug("joystick", "closing joystick %d", nr);
2530 if (sdl_is_controller[nr])
2531 SDL_GameControllerClose(sdl_joystick[nr]);
2533 SDL_JoystickClose(sdl_joystick[nr]);
2535 sdl_joystick[nr] = NULL;
2538 boolean SDLCheckJoystickOpened(int nr)
2540 if (nr < 0 || nr >= MAX_PLAYERS)
2543 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2546 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2548 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2549 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2550 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2551 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2553 if (nr < 0 || nr >= MAX_PLAYERS)
2559 // prevent (slightly jittering, but centered) axis A from resetting axis B
2560 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2561 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2564 sdl_js_axis[nr][axis_id] = axis_value;
2565 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2568 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2570 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2571 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2572 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2573 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2574 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2575 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2576 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2577 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2580 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2581 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2582 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2583 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2584 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2585 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2586 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2587 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2589 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2590 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2591 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2592 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2593 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2595 if (nr < 0 || nr >= MAX_PLAYERS)
2598 if (button_id == -1)
2601 sdl_js_button[nr][button_id] = button_state;
2604 void HandleJoystickEvent(Event *event)
2606 // when using joystick, disable overlay touch buttons
2607 runtime.uses_touch_device = FALSE;
2609 switch (event->type)
2611 case SDL_CONTROLLERDEVICEADDED:
2613 Debug("joystick", "SDL_CONTROLLERDEVICEADDED: device %d added",
2614 event->cdevice.which);
2619 case SDL_CONTROLLERDEVICEREMOVED:
2621 Debug("joystick", "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2622 event->cdevice.which);
2627 case SDL_CONTROLLERAXISMOTION:
2629 Debug("joystick", "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2630 event->caxis.which, event->caxis.axis, event->caxis.value);
2632 setJoystickAxis(event->caxis.which,
2634 event->caxis.value);
2637 case SDL_CONTROLLERBUTTONDOWN:
2639 Debug("joystick", "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2640 event->cbutton.which, event->cbutton.button);
2642 setJoystickButton(event->cbutton.which,
2643 event->cbutton.button,
2647 case SDL_CONTROLLERBUTTONUP:
2649 Debug("joystick", "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2650 event->cbutton.which, event->cbutton.button);
2652 setJoystickButton(event->cbutton.which,
2653 event->cbutton.button,
2657 case SDL_JOYAXISMOTION:
2658 if (sdl_is_controller[event->jaxis.which])
2662 Debug("joystick", "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2663 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2665 if (event->jaxis.axis < 4)
2666 setJoystickAxis(event->jaxis.which,
2668 event->jaxis.value);
2671 case SDL_JOYBUTTONDOWN:
2672 if (sdl_is_controller[event->jaxis.which])
2676 Debug("joystick", "SDL_JOYBUTTONDOWN: 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,
2685 case SDL_JOYBUTTONUP:
2686 if (sdl_is_controller[event->jaxis.which])
2690 Debug("joystick", "SDL_JOYBUTTONUP: device %d, button %d",
2691 event->jbutton.which, event->jbutton.button);
2693 if (event->jbutton.button < 4)
2694 setJoystickButton(event->jbutton.which,
2695 event->jbutton.button,
2704 void SDLInitJoysticks(void)
2706 static boolean sdl_joystick_subsystem_initialized = FALSE;
2707 boolean print_warning = !sdl_joystick_subsystem_initialized;
2708 char *mappings_file_base = getPath2(options.conf_directory,
2709 GAMECONTROLLER_BASENAME);
2710 char *mappings_file_user = getPath2(getUserGameDataDir(),
2711 GAMECONTROLLER_BASENAME);
2715 if (!sdl_joystick_subsystem_initialized)
2717 sdl_joystick_subsystem_initialized = TRUE;
2719 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2721 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2722 Fail("SDL_Init() failed: %s", SDL_GetError());
2724 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2726 // the included game controller base mappings should always be found
2727 if (num_mappings == -1)
2728 Warn("no game controller base mappings found");
2731 Debug("joystick", "%d game controller base mapping(s) added",
2735 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2738 // the personal game controller user mappings may or may not be found
2739 if (num_mappings == -1)
2740 Warn("no game controller user mappings found");
2742 Debug("joystick", , "%d game controller user mapping(s) added",
2745 Debug("joystick", "%d joystick(s) found:", SDL_NumJoysticks());
2748 checked_free(mappings_file_base);
2749 checked_free(mappings_file_user);
2752 for (i = 0; i < SDL_NumJoysticks(); i++)
2754 const char *name, *type;
2756 if (SDL_IsGameController(i))
2758 name = SDL_GameControllerNameForIndex(i);
2759 type = "game controller";
2763 name = SDL_JoystickNameForIndex(i);
2767 Debug("joystick", "- joystick %d (%s): '%s'",
2768 i, type, (name ? name : "(Unknown)"));
2773 // assign joysticks from configured to connected joystick for all players
2774 for (i = 0; i < MAX_PLAYERS; i++)
2776 // get configured joystick for this player
2777 char *device_name = setup.input[i].joy.device_name;
2778 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2780 if (joystick_nr >= SDL_NumJoysticks())
2782 if (setup.input[i].use_joystick && print_warning)
2783 Warn("cannot find joystick %d", joystick_nr);
2788 // store configured joystick number for each player
2789 joystick.nr[i] = joystick_nr;
2792 // now open all connected joysticks (regardless if configured or not)
2793 for (i = 0; i < SDL_NumJoysticks(); i++)
2795 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2796 if (SDLCheckJoystickOpened(i))
2797 SDLCloseJoystick(i);
2799 if (SDLOpenJoystick(i))
2800 joystick.status = JOYSTICK_ACTIVATED;
2801 else if (print_warning)
2802 Warn("cannot open joystick %d", i);
2805 SDLClearJoystickState();
2808 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2810 if (nr < 0 || nr >= MAX_PLAYERS)
2814 *x = sdl_js_axis[nr][0];
2816 *y = sdl_js_axis[nr][1];
2819 *b1 = sdl_js_button[nr][0];
2821 *b2 = sdl_js_button[nr][1];
2827 // ============================================================================
2828 // touch input overlay functions
2829 // ============================================================================
2831 #if defined(USE_TOUCH_INPUT_OVERLAY)
2832 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2835 int grid_xsize = overlay.grid_xsize;
2836 int grid_ysize = overlay.grid_ysize;
2839 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2840 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2842 for (x = 0; x < grid_xsize; x++)
2844 rect.x = (x + 0) * video.screen_width / grid_xsize;
2845 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2847 for (y = 0; y < grid_ysize; y++)
2849 rect.y = (y + 0) * video.screen_height / grid_ysize;
2850 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2852 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2853 SDL_RenderDrawRect(sdl_renderer, &rect);
2857 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2860 static void RenderFillRectangle(int x, int y, int width, int height)
2862 SDL_Rect rect = { x, y, width, height };
2864 SDL_RenderFillRect(sdl_renderer, &rect);
2867 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2869 static int alpha_direction = 0;
2870 static int alpha_highlight = 0;
2871 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2872 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2874 int grid_xsize = overlay.grid_xsize;
2875 int grid_ysize = overlay.grid_ysize;
2878 if (alpha == alpha_max)
2880 if (alpha_direction < 0)
2882 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2884 if (alpha_highlight == 0)
2885 alpha_direction = 1;
2889 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2891 if (alpha_highlight == alpha_max)
2892 alpha_direction = -1;
2897 alpha_direction = 1;
2898 alpha_highlight = alpha;
2901 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2903 for (x = 0; x < grid_xsize; x++)
2905 for (y = 0; y < grid_ysize; y++)
2907 int grid_button = overlay.grid_button[x][y];
2908 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2909 int alpha_draw = alpha;
2910 int outline_border = MV_NONE;
2911 int border_size = 2;
2912 boolean draw_outlined = setup.touch.draw_outlined;
2913 boolean draw_pressed = setup.touch.draw_pressed;
2915 if (grid_button == CHAR_GRID_BUTTON_NONE)
2918 if (grid_button == overlay.grid_button_highlight)
2920 draw_outlined = FALSE;
2921 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2924 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2927 draw_outlined = FALSE;
2929 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2932 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2934 rect.x = (x + 0) * video.screen_width / grid_xsize;
2935 rect.y = (y + 0) * video.screen_height / grid_ysize;
2936 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2937 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2939 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2941 rect.x += border_size;
2942 rect.w -= border_size;
2944 outline_border |= MV_LEFT;
2947 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2949 rect.w -= border_size;
2951 outline_border |= MV_RIGHT;
2954 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2956 rect.y += border_size;
2957 rect.h -= border_size;
2959 outline_border |= MV_UP;
2962 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2964 rect.h -= border_size;
2966 outline_border |= MV_DOWN;
2971 int rect_x = rect.x +
2972 (outline_border & MV_LEFT ? border_size : 0);
2973 int rect_w = rect.w -
2974 (outline_border & MV_LEFT ? border_size : 0) -
2975 (outline_border & MV_RIGHT ? border_size : 0);
2977 if (outline_border & MV_LEFT)
2978 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2980 if (outline_border & MV_RIGHT)
2981 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2982 border_size, rect.h);
2984 if (outline_border & MV_UP)
2985 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2987 if (outline_border & MV_DOWN)
2988 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
2989 rect_w, border_size);
2993 SDL_RenderFillRect(sdl_renderer, &rect);
2998 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3001 static void DrawTouchInputOverlay(void)
3003 static boolean deactivated = TRUE;
3004 static boolean show_grid = FALSE;
3005 static int alpha = 0;
3006 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3007 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3008 boolean active = (overlay.enabled && overlay.active);
3010 if (!active && deactivated)
3015 if (alpha < alpha_max)
3016 alpha = MIN(alpha + alpha_step, alpha_max);
3018 deactivated = FALSE;
3022 alpha = MAX(0, alpha - alpha_step);
3028 if (overlay.show_grid)
3030 else if (deactivated)
3034 DrawTouchInputOverlay_ShowGrid(alpha);
3036 DrawTouchInputOverlay_ShowGridButtons(alpha);
3039 static void DrawTouchGadgetsOverlay(void)
3041 DrawGadgets_OverlayTouchButtons();