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)
75 static DelayCounter update_screen_delay = { 50 }; // (milliseconds)
76 SDL_Surface *screen = backbuffer->surface;
78 if (limit_screen_updates &&
79 !DelayReached(&update_screen_delay))
82 LimitScreenUpdates(FALSE);
86 static int LastFrameCounter = 0;
87 boolean changed = (FrameCounter != LastFrameCounter);
89 Debug("internal:frame", "FrameCounter == %d [%s]", FrameCounter,
90 (changed ? "-" : "SAME FRAME UPDATED"));
92 LastFrameCounter = FrameCounter;
101 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
102 gfx.final_screen_bitmap != NULL) // may not be initialized yet
104 // draw global animations using bitmaps instead of using textures
105 // to prevent texture scaling artefacts (this is potentially slower)
107 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
108 gfx.win_xsize, gfx.win_ysize, 0, 0);
110 FinalizeScreen(DRAW_TO_SCREEN);
112 screen = gfx.final_screen_bitmap->surface;
114 // force full window redraw
118 SDL_Texture *sdl_texture = sdl_texture_stream;
120 // deactivate use of target texture if render targets are not supported
121 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
122 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
123 sdl_texture_target == NULL)
124 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
126 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
127 sdl_texture = sdl_texture_target;
131 int bytes_x = screen->pitch / video.width;
132 int bytes_y = screen->pitch;
134 SDL_UpdateTexture(sdl_texture, rect,
135 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
140 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
143 int xoff = video.screen_xoffset;
144 int yoff = video.screen_yoffset;
145 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
146 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
147 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
149 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
150 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
151 dst_rect2 = &dst_rect_screen;
153 dst_rect1 = &dst_rect_screen;
155 #if defined(HAS_SCREEN_KEYBOARD)
156 SDL_Rect src_rect_up = { 0, 0, video.width, video.height };
157 SDL_Rect dst_rect_up = dst_rect_screen;
159 if (video.shifted_up || video.shifted_up_delay.count)
161 int time_current = SDL_GetTicks();
162 int pos = video.shifted_up_pos;
163 int pos_last = video.shifted_up_pos_last;
165 if (!DelayReachedExt(&video.shifted_up_delay, time_current))
167 int delay_count = time_current - video.shifted_up_delay.count;
168 int delay_value = video.shifted_up_delay.value;
170 pos = pos_last + (pos - pos_last) * delay_count / delay_value;
174 video.shifted_up_pos_last = pos;
175 video.shifted_up_delay.count = 0;
179 src_rect_up.h = video.height - pos;
180 dst_rect_up.h = video.height - pos;
182 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
183 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
185 src_rect2 = &src_rect_up;
186 dst_rect2 = &dst_rect_up;
190 src_rect1 = &src_rect_up;
191 dst_rect1 = &dst_rect_up;
196 // clear render target buffer
197 SDL_RenderClear(sdl_renderer);
199 // set renderer to use target texture for rendering
200 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
201 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
202 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
204 // copy backbuffer texture to render target buffer
205 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
206 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
208 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
209 FinalizeScreen(DRAW_TO_SCREEN);
211 // when using target texture, copy it to screen buffer
212 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
213 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
215 SDL_SetRenderTarget(sdl_renderer, NULL);
216 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
219 #if defined(USE_TOUCH_INPUT_OVERLAY)
220 // draw overlay graphics for touch device input, if needed
221 DrawTouchInputOverlay();
223 // draw overlay gadgets for touch device input, if needed
224 DrawTouchGadgetsOverlay();
227 // global synchronization point of the game to align video frame delay
228 if (with_frame_delay)
229 WaitUntilDelayReached(&video.frame_delay);
231 video.frame_counter++;
233 // show render target buffer on screen
234 SDL_RenderPresent(sdl_renderer);
237 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
239 PumpEvents(); // execute event filter actions while waiting
241 UpdateScreenExt(rect, TRUE);
244 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
246 UpdateScreenExt(rect, FALSE);
249 void Delay_WithScreenUpdates(unsigned int delay)
251 unsigned int time_current = SDL_GetTicks();
252 unsigned int time_delayed = time_current + delay;
254 while (time_current < time_delayed)
256 // updating the screen contains waiting for frame delay (non-busy)
257 UpdateScreen_WithFrameDelay(NULL);
259 time_current = SDL_GetTicks();
263 static void SDLSetWindowIcon(char *basename)
265 // (setting the window icon on Mac OS X would replace the high-quality
266 // dock icon with the currently smaller (and uglier) icon from file)
268 #if !defined(PLATFORM_MAC)
269 char *filename = getCustomImageFilename(basename);
270 SDL_Surface *surface;
272 if (filename == NULL)
274 Warn("SDLSetWindowIcon(): cannot find file '%s'", basename);
279 if ((surface = IMG_Load(filename)) == NULL)
281 Warn("IMG_Load('%s') failed: %s", basename, SDL_GetError());
286 // set transparent color
287 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
288 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
290 SDL_SetWindowIcon(sdl_window, surface);
294 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
295 SDL_PixelFormat *format2)
297 return (format1->format == format2->format &&
298 format1->BitsPerPixel == format2->BitsPerPixel &&
299 format1->BytesPerPixel == format2->BytesPerPixel &&
300 format1->Rmask == format2->Rmask &&
301 format1->Gmask == format2->Gmask &&
302 format1->Bmask == format2->Bmask);
305 static void SDLCopyColorKey(SDL_Surface *src_surface, SDL_Surface *dst_surface)
310 // check if source surface has a color key
311 if (SDL_GetColorKey(src_surface, &color_key) == 0)
313 // get RGB values of color key of source surface
314 SDL_GetRGB(color_key, src_surface->format, &r, &g, &b);
316 // get color key from RGB values in destination surface format
317 color_key = SDL_MapRGB(dst_surface->format, r, g, b);
319 // set color key in destination surface
320 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL, color_key);
324 // unset color key in destination surface
325 SDL_SetColorKey(dst_surface, UNSET_TRANSPARENT_PIXEL, 0);
329 static boolean SDLHasColorKey(SDL_Surface *surface)
333 return (SDL_GetColorKey(surface, &color_key) == 0);
336 static boolean SDLHasAlpha(SDL_Surface *surface)
338 SDL_BlendMode blend_mode;
340 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
343 return (blend_mode == SDL_BLENDMODE_BLEND);
346 void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
348 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
350 SDL_SetSurfaceBlendMode(surface, blend_mode);
351 SDL_SetSurfaceAlphaMod(surface, alpha);
354 const char *SDLGetRendererName(void)
356 static SDL_RendererInfo renderer_info;
358 SDL_GetRendererInfo(sdl_renderer, &renderer_info);
360 return renderer_info.name;
363 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
365 SDL_PixelFormat format;
366 SDL_Surface *new_surface;
371 if (backbuffer && backbuffer->surface)
373 format = *backbuffer->surface->format;
374 format.Amask = surface->format->Amask; // keep alpha channel
378 format = *surface->format;
381 new_surface = SDL_ConvertSurface(surface, &format, 0);
383 if (new_surface == NULL)
384 Fail("SDL_ConvertSurface() failed: %s", SDL_GetError());
386 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
387 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
388 SDLCopyColorKey(surface, new_surface);
393 boolean SDLSetNativeSurface(SDL_Surface **surface)
395 SDL_Surface *new_surface;
397 if (surface == NULL ||
399 backbuffer == NULL ||
400 backbuffer->surface == NULL)
403 // if pixel format already optimized for destination surface, do nothing
404 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
407 new_surface = SDLGetNativeSurface(*surface);
409 SDL_FreeSurface(*surface);
411 *surface = new_surface;
416 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
418 if (program.headless)
421 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
424 Fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
429 void SDLCreateBitmapTextures(Bitmap *bitmap)
435 SDL_DestroyTexture(bitmap->texture);
436 if (bitmap->texture_masked)
437 SDL_DestroyTexture(bitmap->texture_masked);
439 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
440 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
443 void SDLFreeBitmapTextures(Bitmap *bitmap)
449 SDL_DestroyTexture(bitmap->texture);
450 if (bitmap->texture_masked)
451 SDL_DestroyTexture(bitmap->texture_masked);
453 bitmap->texture = NULL;
454 bitmap->texture_masked = NULL;
457 void SDLInitVideoDisplay(void)
459 // set hint to select render driver as specified in setup config file
460 if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
461 SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
463 // initialize SDL video
464 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
465 Fail("SDL_InitSubSystem() failed: %s", SDL_GetError());
467 // set default SDL depth
468 video.default_depth = 32; // (how to determine video depth in SDL2?)
470 // Code used with SDL 1.2:
471 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
474 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
476 if (program.headless)
479 video.window_scaling_percent = setup.window_scaling_percent;
480 video.window_scaling_quality = setup.window_scaling_quality;
482 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
484 // SDL 2.0: support for (desktop) fullscreen mode available
485 video.fullscreen_available = TRUE;
487 // open SDL video output device (window or fullscreen mode)
488 if (!SDLSetVideoMode(fullscreen))
489 Fail("setting video mode failed");
491 // !!! SDL2 can only set the window icon if the window already exists !!!
493 SDLSetWindowIcon(program.icon_filename);
495 // set window and icon title
499 static void SDLInitVideoBuffer_DrawBuffer(void)
501 /* SDL cannot directly draw to the visible video framebuffer like X11,
502 but always uses a backbuffer, which is then blitted to the visible
503 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
504 visible video framebuffer with 'SDL_Flip', if the hardware supports
505 this). Therefore do not use an additional backbuffer for drawing, but
506 use a symbolic buffer (distinguishable from the SDL backbuffer) called
507 'window', which indicates that the SDL backbuffer should be updated to
508 the visible video framebuffer when attempting to blit to it.
510 For convenience, it seems to be a good idea to create this symbolic
511 buffer 'window' at the same size as the SDL backbuffer. Although it
512 should never be drawn to directly, it would do no harm nevertheless. */
514 // create additional (symbolic) buffer for double-buffering
515 ReCreateBitmap(&window, video.width, video.height);
517 // create dummy drawing buffer for headless mode, if needed
518 if (program.headless)
519 ReCreateBitmap(&backbuffer, video.width, video.height);
522 void SDLInitVideoBuffer(boolean fullscreen)
524 SDLInitVideoBuffer_VideoBuffer(fullscreen);
525 SDLInitVideoBuffer_DrawBuffer();
528 static boolean SDLCreateScreen(boolean fullscreen)
530 SDL_Surface *new_surface = NULL;
532 int surface_flags_window = SURFACE_FLAGS;
533 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
536 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
538 video.vsync_mode = VSYNC_MODE_OFF;
540 if (!strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF))
542 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
543 video.vsync_mode = VSYNC_MODE_NORMAL;
546 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
547 _without_ enabling 2D/3D acceleration and/or guest additions installed,
548 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
549 it will try to use accelerated graphics and apparently fails miserably) */
550 int renderer_flags = SDL_RENDERER_SOFTWARE;
553 int width = video.width;
554 int height = video.height;
555 int screen_width = video.screen_width;
556 int screen_height = video.screen_height;
557 int surface_flags = (fullscreen ? surface_flags_fullscreen :
558 surface_flags_window);
559 int display_nr = options.display_nr;
561 // default window size is unscaled
562 video.window_width = screen_width;
563 video.window_height = screen_height;
565 // store if initial screen mode is fullscreen mode when changing screen size
566 video.fullscreen_initial = fullscreen;
568 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
570 video.window_width = window_scaling_factor * screen_width;
571 video.window_height = window_scaling_factor * screen_height;
573 if (sdl_texture_stream)
575 SDL_DestroyTexture(sdl_texture_stream);
576 sdl_texture_stream = NULL;
579 if (sdl_texture_target)
581 SDL_DestroyTexture(sdl_texture_target);
582 sdl_texture_target = NULL;
585 if (!(fullscreen && fullscreen_enabled))
589 SDL_DestroyRenderer(sdl_renderer);
595 SDL_SetWindowSize(sdl_window, video.window_width, video.window_height);
599 if (sdl_window == NULL)
600 sdl_window = SDL_CreateWindow(program.window_title,
601 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
602 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
607 if (sdl_window != NULL)
609 if (sdl_renderer == NULL)
610 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
612 if (sdl_renderer != NULL)
614 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
615 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
617 // required for setting adaptive vsync when using OpenGL renderer
618 SDLSetScreenVsyncMode(setup.vsync_mode);
620 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
621 SDL_PIXELFORMAT_ARGB8888,
622 SDL_TEXTUREACCESS_STREAMING,
625 if (SDL_RenderTargetSupported(sdl_renderer))
626 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
627 SDL_PIXELFORMAT_ARGB8888,
628 SDL_TEXTUREACCESS_TARGET,
631 if (sdl_texture_stream != NULL)
633 // use SDL default values for RGB masks and no alpha channel
634 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
636 if (new_surface == NULL)
637 Warn("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
641 Warn("SDL_CreateTexture() failed: %s", SDL_GetError());
646 Warn("SDL_CreateRenderer() failed: %s", SDL_GetError());
651 Warn("SDL_CreateWindow() failed: %s", SDL_GetError());
654 SDLSetScreenProperties();
656 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
657 if (new_surface != NULL)
658 fullscreen_enabled = fullscreen;
660 if (backbuffer == NULL)
661 backbuffer = CreateBitmapStruct();
663 backbuffer->width = video.width;
664 backbuffer->height = video.height;
666 if (backbuffer->surface)
667 SDL_FreeSurface(backbuffer->surface);
669 backbuffer->surface = new_surface;
671 return (new_surface != NULL);
674 boolean SDLSetVideoMode(boolean fullscreen)
676 boolean success = FALSE;
680 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
682 // switch display to fullscreen mode, if available
683 success = SDLCreateScreen(TRUE);
687 // switching display to fullscreen mode failed -- do not try it again
688 video.fullscreen_available = FALSE;
692 video.fullscreen_enabled = TRUE;
696 if ((!fullscreen && video.fullscreen_enabled) || !success)
698 // switch display to window mode
699 success = SDLCreateScreen(FALSE);
703 // switching display to window mode failed -- should not happen
707 video.fullscreen_enabled = FALSE;
708 video.window_scaling_percent = setup.window_scaling_percent;
709 video.window_scaling_quality = setup.window_scaling_quality;
711 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
715 SDLRedrawWindow(); // map window
720 void SDLSetWindowTitle(void)
722 if (sdl_window == NULL)
725 SDL_SetWindowTitle(sdl_window, program.window_title);
728 void SDLSetWindowScaling(int window_scaling_percent)
730 if (sdl_window == NULL)
733 float window_scaling_factor = (float)window_scaling_percent / 100;
734 int new_window_width = (int)(window_scaling_factor * video.screen_width);
735 int new_window_height = (int)(window_scaling_factor * video.screen_height);
737 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
739 video.window_scaling_percent = window_scaling_percent;
740 video.window_width = new_window_width;
741 video.window_height = new_window_height;
746 void SDLSetWindowScalingQuality(char *window_scaling_quality)
748 SDL_Texture *new_texture;
750 if (sdl_texture_stream == NULL)
753 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
755 new_texture = SDL_CreateTexture(sdl_renderer,
756 SDL_PIXELFORMAT_ARGB8888,
757 SDL_TEXTUREACCESS_STREAMING,
758 video.width, video.height);
760 if (new_texture != NULL)
762 SDL_DestroyTexture(sdl_texture_stream);
764 sdl_texture_stream = new_texture;
767 if (SDL_RenderTargetSupported(sdl_renderer))
768 new_texture = SDL_CreateTexture(sdl_renderer,
769 SDL_PIXELFORMAT_ARGB8888,
770 SDL_TEXTUREACCESS_TARGET,
771 video.width, video.height);
775 if (new_texture != NULL)
777 SDL_DestroyTexture(sdl_texture_target);
779 sdl_texture_target = new_texture;
784 video.window_scaling_quality = window_scaling_quality;
787 void SDLSetWindowFullscreen(boolean fullscreen)
789 if (sdl_window == NULL)
792 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
794 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
795 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
797 // if screen size was changed in fullscreen mode, correct desktop window size
798 if (!fullscreen && video.fullscreen_initial)
800 SDLSetWindowScaling(setup.window_scaling_percent);
801 SDL_SetWindowPosition(sdl_window,
802 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr),
803 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr));
805 video.fullscreen_initial = FALSE;
809 void SDLSetDisplaySize(void)
811 if (sdl_renderer != NULL)
815 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
817 video.display_width = w;
818 video.display_height = h;
821 Debug("video", "SDL renderer size: %d x %d",
822 video.display_width, video.display_height);
827 SDL_Rect display_bounds;
829 SDL_GetDisplayBounds(0, &display_bounds);
831 video.display_width = display_bounds.w;
832 video.display_height = display_bounds.h;
835 Debug("video", "SDL display size: %d x %d",
836 video.display_width, video.display_height);
841 void SDLSetScreenSizeAndOffsets(int width, int height)
843 // set default video screen size and offsets
844 video.screen_width = width;
845 video.screen_height = height;
846 video.screen_xoffset = 0;
847 video.screen_yoffset = 0;
849 #if defined(USE_COMPLETE_DISPLAY)
850 float ratio_video = (float) width / height;
851 float ratio_display = (float) video.display_width / video.display_height;
853 if (ratio_video != ratio_display)
855 // adjust drawable screen size to cover the whole device display
857 if (ratio_video < ratio_display)
858 video.screen_width *= ratio_display / ratio_video;
860 video.screen_height *= ratio_video / ratio_display;
862 video.screen_xoffset = (video.screen_width - width) / 2;
863 video.screen_yoffset = (video.screen_height - height) / 2;
866 Debug("video", "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
868 video.screen_width, video.screen_height,
869 ratio_video, ratio_display);
875 void SDLSetScreenSizeForRenderer(int width, int height)
877 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
880 void SDLSetScreenProperties(void)
883 SDLSetScreenSizeAndOffsets(video.width, video.height);
884 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
886 SetOverlayGridSizeAndButtons();
889 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
891 video.screen_rendering_mode =
892 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
893 SPECIAL_RENDERING_BITMAP :
894 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
895 SPECIAL_RENDERING_TARGET:
896 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
897 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
900 void SDLSetScreenVsyncMode(char *vsync_mode)
902 // changing vsync mode without re-creating renderer only supported by OpenGL
903 if (!strPrefixLower((char *)SDLGetRendererName(), "opengl"))
906 int interval = VSYNC_MODE_STR_TO_INT(vsync_mode);
907 int result = SDL_GL_SetSwapInterval(interval);
909 // if adaptive vsync requested, but not supported, retry with normal vsync
910 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
912 interval = VSYNC_MODE_NORMAL;
914 result = SDL_GL_SetSwapInterval(interval);
918 interval = VSYNC_MODE_OFF;
920 video.vsync_mode = interval;
923 void SDLRedrawWindow(void)
925 UpdateScreen_WithoutFrameDelay(NULL);
928 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
931 if (program.headless)
934 SDL_Surface *surface =
935 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
938 Fail("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
940 SDLSetNativeSurface(&surface);
942 bitmap->surface = surface;
945 void SDLFreeBitmapPointers(Bitmap *bitmap)
948 SDL_FreeSurface(bitmap->surface);
949 if (bitmap->surface_masked)
950 SDL_FreeSurface(bitmap->surface_masked);
952 bitmap->surface = NULL;
953 bitmap->surface_masked = NULL;
956 SDL_DestroyTexture(bitmap->texture);
957 if (bitmap->texture_masked)
958 SDL_DestroyTexture(bitmap->texture_masked);
960 bitmap->texture = NULL;
961 bitmap->texture_masked = NULL;
964 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
965 int src_x, int src_y, int width, int height,
966 int dst_x, int dst_y, int mask_mode)
968 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
969 SDL_Rect src_rect, dst_rect;
981 // if (src_bitmap != backbuffer || dst_bitmap != window)
982 if (!(src_bitmap == backbuffer && dst_bitmap == window))
983 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
984 src_bitmap->surface_masked : src_bitmap->surface),
985 &src_rect, real_dst_bitmap->surface, &dst_rect);
987 if (dst_bitmap == window)
988 UpdateScreen_WithFrameDelay(&dst_rect);
991 void SDLBlitTexture(Bitmap *bitmap,
992 int src_x, int src_y, int width, int height,
993 int dst_x, int dst_y, int mask_mode)
995 SDL_Texture *texture;
1000 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1002 if (texture == NULL)
1008 src_rect.h = height;
1013 dst_rect.h = height;
1015 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1018 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1021 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1029 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1031 if (dst_bitmap == window)
1032 UpdateScreen_WithFrameDelay(&rect);
1035 void PrepareFadeBitmap(int draw_target)
1037 Bitmap *fade_bitmap =
1038 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1039 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1041 if (fade_bitmap == NULL)
1044 // copy backbuffer to fading buffer
1045 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1047 // add border and animations to fading buffer
1048 FinalizeScreen(draw_target);
1051 void SDLFadeRectangle(int x, int y, int width, int height,
1052 int fade_mode, int fade_delay, int post_delay,
1053 void (*draw_border_function)(void))
1055 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1056 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1057 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1058 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1059 SDL_Surface *surface_screen = backbuffer->surface;
1060 SDL_Rect src_rect, dst_rect;
1062 int src_x = x, src_y = y;
1063 int dst_x = x, dst_y = y;
1064 unsigned int time_last, time_current;
1066 // store function for drawing global masked border
1067 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1069 // deactivate drawing of global border while fading, if needed
1070 if (draw_border_function == NULL)
1071 gfx.draw_global_border_function = NULL;
1076 src_rect.h = height;
1080 dst_rect.w = width; // (ignored)
1081 dst_rect.h = height; // (ignored)
1083 dst_rect2 = dst_rect;
1085 // before fading in, store backbuffer (without animation graphics)
1086 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1087 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1089 // copy source and target surfaces to temporary surfaces for fading
1090 if (fade_mode & FADE_TYPE_TRANSFORM)
1092 // (source and target fading buffer already prepared)
1094 else if (fade_mode & FADE_TYPE_FADE_IN)
1096 // (target fading buffer already prepared)
1097 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1099 else // FADE_TYPE_FADE_OUT
1101 // (source fading buffer already prepared)
1102 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1105 time_current = SDL_GetTicks();
1107 if (fade_delay <= 0)
1109 // immediately draw final target frame without delay
1110 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1114 // when fading without delay, also skip post delay
1118 if (fade_mode == FADE_MODE_MELT)
1120 boolean done = FALSE;
1121 int melt_pixels = 2;
1122 int melt_columns = width / melt_pixels;
1123 int ypos[melt_columns];
1124 int max_steps = height / 8 + 32;
1129 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1131 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1133 ypos[0] = -GetSimpleRandom(16);
1135 for (i = 1 ; i < melt_columns; i++)
1137 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1139 ypos[i] = ypos[i - 1] + r;
1152 time_last = time_current;
1153 time_current = SDL_GetTicks();
1154 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1155 steps_final = MIN(MAX(0, steps), max_steps);
1159 done = (steps_done >= steps_final);
1161 for (i = 0 ; i < melt_columns; i++)
1169 else if (ypos[i] < height)
1174 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1176 if (ypos[i] + dy >= height)
1177 dy = height - ypos[i];
1179 // copy part of (appearing) target surface to upper area
1180 src_rect.x = src_x + i * melt_pixels;
1181 // src_rect.y = src_y + ypos[i];
1183 src_rect.w = melt_pixels;
1185 src_rect.h = ypos[i] + dy;
1187 dst_rect.x = dst_x + i * melt_pixels;
1188 // dst_rect.y = dst_y + ypos[i];
1191 if (steps_done >= steps_final)
1192 SDL_BlitSurface(surface_target, &src_rect,
1193 surface_screen, &dst_rect);
1197 // copy part of (disappearing) source surface to lower area
1198 src_rect.x = src_x + i * melt_pixels;
1200 src_rect.w = melt_pixels;
1201 src_rect.h = height - ypos[i];
1203 dst_rect.x = dst_x + i * melt_pixels;
1204 dst_rect.y = dst_y + ypos[i];
1206 if (steps_done >= steps_final)
1207 SDL_BlitSurface(surface_source, &src_rect,
1208 surface_screen, &dst_rect);
1214 src_rect.x = src_x + i * melt_pixels;
1216 src_rect.w = melt_pixels;
1217 src_rect.h = height;
1219 dst_rect.x = dst_x + i * melt_pixels;
1222 if (steps_done >= steps_final)
1223 SDL_BlitSurface(surface_target, &src_rect,
1224 surface_screen, &dst_rect);
1228 if (steps_done >= steps_final)
1230 if (draw_border_function != NULL)
1231 draw_border_function();
1233 UpdateScreen_WithFrameDelay(&dst_rect2);
1235 if (PendingEscapeKeyEvent())
1240 else if (fade_mode == FADE_MODE_CURTAIN)
1244 int xx_size = width / 2;
1246 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1248 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1250 for (xx = 0; xx < xx_size;)
1252 time_last = time_current;
1253 time_current = SDL_GetTicks();
1254 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1255 xx_final = MIN(MAX(0, xx), xx_size);
1260 src_rect.h = height;
1265 // draw new (target) image to screen buffer
1266 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1268 if (xx_final < xx_size)
1270 src_rect.w = xx_size - xx_final;
1271 src_rect.h = height;
1273 // draw old (source) image to screen buffer (left side)
1275 src_rect.x = src_x + xx_final;
1278 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1280 // draw old (source) image to screen buffer (right side)
1282 src_rect.x = src_x + xx_size;
1283 dst_rect.x = dst_x + xx_size + xx_final;
1285 SDL_BlitSurface(surface_source, &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_rect2);
1294 if (PendingEscapeKeyEvent())
1298 else // fading in, fading out or cross-fading
1303 for (alpha = 0.0; alpha < 255.0;)
1305 time_last = time_current;
1306 time_current = SDL_GetTicks();
1307 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1308 alpha_final = MIN(MAX(0, alpha), 255);
1310 // draw existing (source) image to screen buffer
1311 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1313 // draw new (target) image to screen buffer using alpha blending
1314 SDLSetAlpha(surface_target, TRUE, alpha_final);
1315 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1317 if (draw_border_function != NULL)
1318 draw_border_function();
1320 // only update the region of the screen that is affected from fading
1321 UpdateScreen_WithFrameDelay(&dst_rect);
1323 if (PendingEscapeKeyEvent())
1329 Delay_WithScreenUpdates(post_delay);
1331 // restore function for drawing global masked border
1332 gfx.draw_global_border_function = draw_global_border_function;
1334 // after fading in, restore backbuffer (without animation graphics)
1335 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1336 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1339 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1340 int to_x, int to_y, Uint32 color)
1342 SDL_Surface *surface = dst_bitmap->surface;
1346 swap_numbers(&from_x, &to_x);
1349 swap_numbers(&from_y, &to_y);
1353 rect.w = (to_x - from_x + 1);
1354 rect.h = (to_y - from_y + 1);
1356 SDL_FillRect(surface, &rect, color);
1359 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1360 int to_x, int to_y, Uint32 color)
1362 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1365 #if ENABLE_UNUSED_CODE
1366 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1367 int num_points, Uint32 color)
1372 for (i = 0; i < num_points - 1; i++)
1374 for (x = 0; x < line_width; x++)
1376 for (y = 0; y < line_width; y++)
1378 int dx = x - line_width / 2;
1379 int dy = y - line_width / 2;
1381 if ((x == 0 && y == 0) ||
1382 (x == 0 && y == line_width - 1) ||
1383 (x == line_width - 1 && y == 0) ||
1384 (x == line_width - 1 && y == line_width - 1))
1387 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1388 points[i+1].x + dx, points[i+1].y + dy, color);
1395 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1397 SDL_Surface *surface = src_bitmap->surface;
1399 switch (surface->format->BytesPerPixel)
1401 case 1: // assuming 8-bpp
1403 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1407 case 2: // probably 15-bpp or 16-bpp
1409 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1413 case 3: // slow 24-bpp mode; usually not used
1416 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1420 shift = surface->format->Rshift;
1421 color |= *(pix + shift / 8) >> shift;
1422 shift = surface->format->Gshift;
1423 color |= *(pix + shift / 8) >> shift;
1424 shift = surface->format->Bshift;
1425 color |= *(pix + shift / 8) >> shift;
1431 case 4: // probably 32-bpp
1433 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1442 // ============================================================================
1443 // The following functions were taken from the SGE library
1444 // (SDL Graphics Extension Library) by Anders Lindström
1445 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1446 // ============================================================================
1448 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1450 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1452 switch (surface->format->BytesPerPixel)
1457 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1463 // Probably 15-bpp or 16-bpp
1464 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1470 // Slow 24-bpp mode, usually not used
1474 // Gack - slow, but endian correct
1475 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1476 shift = surface->format->Rshift;
1477 *(pix+shift/8) = color>>shift;
1478 shift = surface->format->Gshift;
1479 *(pix+shift/8) = color>>shift;
1480 shift = surface->format->Bshift;
1481 *(pix+shift/8) = color>>shift;
1488 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1496 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1497 Uint8 R, Uint8 G, Uint8 B)
1499 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1502 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1504 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1507 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1509 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1512 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1517 // Gack - slow, but endian correct
1518 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1519 shift = surface->format->Rshift;
1520 *(pix+shift/8) = color>>shift;
1521 shift = surface->format->Gshift;
1522 *(pix+shift/8) = color>>shift;
1523 shift = surface->format->Bshift;
1524 *(pix+shift/8) = color>>shift;
1527 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1529 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1532 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1534 switch (dest->format->BytesPerPixel)
1537 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1541 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1545 _PutPixel24(dest,x,y,color);
1549 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1555 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1557 if (SDL_MUSTLOCK(surface))
1559 if (SDL_LockSurface(surface) < 0)
1565 _PutPixel(surface, x, y, color);
1567 if (SDL_MUSTLOCK(surface))
1569 SDL_UnlockSurface(surface);
1574 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1575 Uint8 r, Uint8 g, Uint8 b)
1577 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1580 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1582 if (y >= 0 && y <= dest->h - 1)
1584 switch (dest->format->BytesPerPixel)
1587 return y*dest->pitch;
1591 return y*dest->pitch/2;
1595 return y*dest->pitch;
1599 return y*dest->pitch/4;
1607 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1610 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1612 switch (surface->format->BytesPerPixel)
1617 *((Uint8 *)surface->pixels + ypitch + x) = color;
1623 // Probably 15-bpp or 16-bpp
1624 *((Uint16 *)surface->pixels + ypitch + x) = color;
1630 // Slow 24-bpp mode, usually not used
1634 // Gack - slow, but endian correct
1635 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1636 shift = surface->format->Rshift;
1637 *(pix+shift/8) = color>>shift;
1638 shift = surface->format->Gshift;
1639 *(pix+shift/8) = color>>shift;
1640 shift = surface->format->Bshift;
1641 *(pix+shift/8) = color>>shift;
1648 *((Uint32 *)surface->pixels + ypitch + x) = color;
1655 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1660 if (SDL_MUSTLOCK(Surface))
1662 if (SDL_LockSurface(Surface) < 0)
1676 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1680 if (x2 > Surface->w - 1)
1681 x2 = Surface->w - 1;
1688 SDL_FillRect(Surface, &l, Color);
1690 if (SDL_MUSTLOCK(Surface))
1692 SDL_UnlockSurface(Surface);
1696 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1697 Uint8 R, Uint8 G, Uint8 B)
1699 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1702 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1715 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1719 if (x2 > Surface->w - 1)
1720 x2 = Surface->w - 1;
1727 SDL_FillRect(Surface, &l, Color);
1730 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1735 if (SDL_MUSTLOCK(Surface))
1737 if (SDL_LockSurface(Surface) < 0)
1751 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1755 if (y2 > Surface->h - 1)
1756 y2 = Surface->h - 1;
1763 SDL_FillRect(Surface, &l, Color);
1765 if (SDL_MUSTLOCK(Surface))
1767 SDL_UnlockSurface(Surface);
1771 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1772 Uint8 R, Uint8 G, Uint8 B)
1774 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1777 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1790 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1794 if (y2 > Surface->h - 1)
1795 y2 = Surface->h - 1;
1802 SDL_FillRect(Surface, &l, Color);
1806 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1807 Sint16 x2, Sint16 y2, Uint32 Color,
1808 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1811 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1816 sdx = (dx < 0) ? -1 : 1;
1817 sdy = (dy < 0) ? -1 : 1;
1829 for (x = 0; x < dx; x++)
1831 Callback(Surface, px, py, Color);
1845 for (y = 0; y < dy; y++)
1847 Callback(Surface, px, py, Color);
1862 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1863 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1864 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1867 sge_DoLine(Surface, X1, Y1, X2, Y2,
1868 SDL_MapRGB(Surface->format, R, G, B), Callback);
1872 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1875 if (SDL_MUSTLOCK(Surface))
1877 if (SDL_LockSurface(Surface) < 0)
1882 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1884 // unlock the display
1885 if (SDL_MUSTLOCK(Surface))
1887 SDL_UnlockSurface(Surface);
1892 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1893 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1895 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1899 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1901 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1905 // ----------------------------------------------------------------------------
1906 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1907 // ----------------------------------------------------------------------------
1909 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1910 int src_x, int src_y, int width, int height,
1911 int dst_x, int dst_y)
1915 for (y = 0; y < height; y++)
1917 for (x = 0; x < width; x++)
1919 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1921 if (pixel != BLACK_PIXEL)
1922 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1928 // ============================================================================
1929 // The following functions were taken from the SDL_gfx library version 2.0.3
1930 // (Rotozoomer) by Andreas Schiffler
1931 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1932 // ============================================================================
1934 // ----------------------------------------------------------------------------
1937 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1938 // ----------------------------------------------------------------------------
1948 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1951 tColorRGBA *sp, *csp, *dp;
1955 sp = csp = (tColorRGBA *) src->pixels;
1956 dp = (tColorRGBA *) dst->pixels;
1957 dgap = dst->pitch - dst->w * 4;
1959 for (y = 0; y < dst->h; y++)
1963 for (x = 0; x < dst->w; x++)
1965 tColorRGBA *sp0 = sp;
1966 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1967 tColorRGBA *sp00 = &sp0[0];
1968 tColorRGBA *sp01 = &sp0[1];
1969 tColorRGBA *sp10 = &sp1[0];
1970 tColorRGBA *sp11 = &sp1[1];
1973 // create new color pixel from all four source color pixels
1974 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1975 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1976 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1977 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1982 // advance source pointers
1985 // advance destination pointer
1989 // advance source pointer
1990 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1992 // advance destination pointers
1993 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1999 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2001 int x, y, *sax, *say, *csax, *csay;
2003 tColorRGBA *sp, *csp, *csp0, *dp;
2006 // use specialized zoom function when scaling down to exactly half size
2007 if (src->w == 2 * dst->w &&
2008 src->h == 2 * dst->h)
2009 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2012 sx = (float) src->w / (float) dst->w;
2013 sy = (float) src->h / (float) dst->h;
2015 // allocate memory for row increments
2016 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2017 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2019 // precalculate row increments
2020 for (x = 0; x <= dst->w; x++)
2021 *csax++ = (int)(sx * x);
2023 for (y = 0; y <= dst->h; y++)
2024 *csay++ = (int)(sy * y);
2027 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2028 dp = (tColorRGBA *) dst->pixels;
2029 dgap = dst->pitch - dst->w * 4;
2032 for (y = 0; y < dst->h; y++)
2037 for (x = 0; x < dst->w; x++)
2042 // advance source pointers
2046 // advance destination pointer
2050 // advance source pointer
2052 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2054 // advance destination pointers
2055 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2064 // ----------------------------------------------------------------------------
2067 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2068 // ----------------------------------------------------------------------------
2070 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2072 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2073 Uint8 *sp, *dp, *csp;
2077 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2078 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2080 // allocate memory for row increments
2081 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2082 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2084 // precalculate row increments
2087 for (x = 0; x < dst->w; x++)
2090 *csax = (csx >> 16);
2097 for (y = 0; y < dst->h; y++)
2100 *csay = (csy >> 16);
2107 for (x = 0; x < dst->w; x++)
2115 for (y = 0; y < dst->h; y++)
2122 sp = csp = (Uint8 *) src->pixels;
2123 dp = (Uint8 *) dst->pixels;
2124 dgap = dst->pitch - dst->w;
2128 for (y = 0; y < dst->h; y++)
2132 for (x = 0; x < dst->w; x++)
2137 // advance source pointers
2141 // advance destination pointer
2145 // advance source pointer (for row)
2146 csp += ((*csay) * src->pitch);
2149 // advance destination pointers
2159 // ----------------------------------------------------------------------------
2162 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2163 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2164 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2165 // into a 32bit RGBA format on the fly.
2166 // ----------------------------------------------------------------------------
2168 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2170 SDL_Surface *zoom_src = NULL;
2171 SDL_Surface *zoom_dst = NULL;
2172 boolean is_converted = FALSE;
2179 // determine if source surface is 32 bit or 8 bit
2180 is_32bit = (src->format->BitsPerPixel == 32);
2182 if (is_32bit || src->format->BitsPerPixel == 8)
2184 // use source surface 'as is'
2189 // new source surface is 32 bit with a defined RGB ordering
2190 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2191 0x000000ff, 0x0000ff00, 0x00ff0000,
2192 (src->format->Amask ? 0xff000000 : 0));
2193 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2195 is_converted = TRUE;
2198 // allocate surface to completely contain the zoomed surface
2201 // target surface is 32 bit with source RGBA/ABGR ordering
2202 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2203 zoom_src->format->Rmask,
2204 zoom_src->format->Gmask,
2205 zoom_src->format->Bmask,
2206 zoom_src->format->Amask);
2210 // target surface is 8 bit
2211 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2215 // lock source surface
2216 SDL_LockSurface(zoom_src);
2218 // check which kind of surface we have
2221 // call the 32 bit transformation routine to do the zooming
2222 zoomSurfaceRGBA(zoom_src, zoom_dst);
2227 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2228 zoom_dst->format->palette->colors[i] =
2229 zoom_src->format->palette->colors[i];
2230 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2232 // call the 8 bit transformation routine to do the zooming
2233 zoomSurfaceY(zoom_src, zoom_dst);
2236 // unlock source surface
2237 SDL_UnlockSurface(zoom_src);
2239 // free temporary surface
2241 SDL_FreeSurface(zoom_src);
2243 // return destination surface
2247 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2249 SDL_Surface *new_surface;
2251 if (surface == NULL)
2254 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2255 Fail("SDLGetNativeSurface() failed");
2257 // remove alpha channel from native non-transparent surface, if defined
2258 SDLSetAlpha(new_surface, FALSE, 0);
2260 // remove transparent color from native non-transparent surface, if defined
2261 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2266 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2268 Bitmap *dst_bitmap = CreateBitmapStruct();
2269 SDL_Surface *src_surface = src_bitmap->surface_masked;
2270 SDL_Surface *dst_surface;
2272 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2273 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2275 dst_bitmap->width = dst_width;
2276 dst_bitmap->height = dst_height;
2278 // create zoomed temporary surface from source surface
2279 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2281 // create native format destination surface from zoomed temporary surface
2282 SDLSetNativeSurface(&dst_surface);
2284 // set color key for zoomed surface from source surface, if defined
2285 if (SDLHasColorKey(src_surface))
2286 SDLCopyColorKey(src_surface, dst_surface);
2288 // create native non-transparent surface for opaque blitting
2289 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2291 // set native transparent surface for masked blitting
2292 dst_bitmap->surface_masked = dst_surface;
2298 // ============================================================================
2299 // load image to bitmap
2300 // ============================================================================
2302 Bitmap *SDLLoadImage(char *filename)
2304 Bitmap *new_bitmap = CreateBitmapStruct();
2305 SDL_Surface *sdl_image_tmp;
2307 if (program.headless)
2309 // prevent sanity check warnings at later stage
2310 new_bitmap->width = new_bitmap->height = 1;
2315 print_timestamp_init("SDLLoadImage");
2317 print_timestamp_time(getBaseNamePtr(filename));
2319 // load image to temporary surface
2320 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2321 Fail("IMG_Load('%s') failed: %s", getBaseNamePtr(filename), SDL_GetError());
2323 print_timestamp_time("IMG_Load");
2325 UPDATE_BUSY_STATE();
2327 // create native non-transparent surface for current image
2328 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2329 Fail("SDLGetOpaqueSurface() failed");
2331 print_timestamp_time("SDLGetNativeSurface (opaque)");
2333 UPDATE_BUSY_STATE();
2335 // set black pixel to transparent if no alpha channel / transparent color
2336 if (!SDLHasAlpha(sdl_image_tmp) &&
2337 !SDLHasColorKey(sdl_image_tmp))
2338 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2339 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2341 // create native transparent surface for current image
2342 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2343 Fail("SDLGetNativeSurface() failed");
2345 print_timestamp_time("SDLGetNativeSurface (masked)");
2347 UPDATE_BUSY_STATE();
2349 // free temporary surface
2350 SDL_FreeSurface(sdl_image_tmp);
2352 new_bitmap->width = new_bitmap->surface->w;
2353 new_bitmap->height = new_bitmap->surface->h;
2355 print_timestamp_done("SDLLoadImage");
2361 // ----------------------------------------------------------------------------
2362 // custom cursor fuctions
2363 // ----------------------------------------------------------------------------
2365 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2367 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2368 cursor_info->width, cursor_info->height,
2369 cursor_info->hot_x, cursor_info->hot_y);
2372 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2374 static struct MouseCursorInfo *last_cursor_info = NULL;
2375 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2376 static SDL_Cursor *cursor_default = NULL;
2377 static SDL_Cursor *cursor_current = NULL;
2379 // if invoked for the first time, store the SDL default cursor
2380 if (cursor_default == NULL)
2381 cursor_default = SDL_GetCursor();
2383 // only create new cursor if cursor info (custom only) has changed
2384 if (cursor_info != NULL && cursor_info != last_cursor_info)
2386 cursor_current = create_cursor(cursor_info);
2387 last_cursor_info = cursor_info;
2390 // only set new cursor if cursor info (custom or NULL) has changed
2391 if (cursor_info != last_cursor_info2)
2392 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2394 last_cursor_info2 = cursor_info;
2398 // ============================================================================
2400 // ============================================================================
2402 void SDLOpenAudio(void)
2404 if (program.headless)
2407 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2409 Warn("SDL_InitSubSystem() failed: %s", SDL_GetError());
2414 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2415 AUDIO_NUM_CHANNELS_STEREO,
2416 setup.system.audio_fragment_size) < 0)
2418 Warn("Mix_OpenAudio() failed: %s", SDL_GetError());
2423 audio.sound_available = TRUE;
2424 audio.music_available = TRUE;
2425 audio.loops_available = TRUE;
2426 audio.sound_enabled = TRUE;
2428 // set number of available mixer channels
2429 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2430 audio.music_channel = MUSIC_CHANNEL;
2431 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2433 Mixer_InitChannels();
2436 void SDLCloseAudio(void)
2439 Mix_HaltChannel(-1);
2442 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2446 // ============================================================================
2448 // ============================================================================
2450 void SDLWaitEvent(Event *event)
2452 SDL_WaitEvent(event);
2455 void SDLCorrectRawMousePosition(int *x, int *y)
2457 if (sdl_renderer == NULL)
2460 // this corrects the raw mouse position for logical screen size within event
2461 // filters (correction done later by SDL library when handling mouse events)
2464 float scale_x, scale_y;
2466 SDL_RenderGetViewport(sdl_renderer, &viewport);
2467 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2469 *x = (int)(*x / scale_x);
2470 *y = (int)(*y / scale_y);
2477 // ============================================================================
2478 // joystick functions
2479 // ============================================================================
2481 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2482 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2483 static int sdl_js_axis[MAX_PLAYERS][2];
2484 static int sdl_js_button[MAX_PLAYERS][2];
2485 static boolean sdl_is_controller[MAX_PLAYERS];
2487 void SDLClearJoystickState(void)
2491 for (i = 0; i < MAX_PLAYERS; i++)
2493 for (j = 0; j < 2; j++)
2495 sdl_js_axis_raw[i][j] = -1;
2496 sdl_js_axis[i][j] = 0;
2497 sdl_js_button[i][j] = 0;
2502 boolean SDLOpenJoystick(int nr)
2504 if (nr < 0 || nr >= MAX_PLAYERS)
2507 sdl_is_controller[nr] = SDL_IsGameController(nr);
2510 Debug("joystick", "opening joystick %d (%s)",
2511 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2514 if (sdl_is_controller[nr])
2515 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2517 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2519 return (sdl_joystick[nr] != NULL);
2522 void SDLCloseJoystick(int nr)
2524 if (nr < 0 || nr >= MAX_PLAYERS)
2528 Debug("joystick", "closing joystick %d", nr);
2531 if (sdl_is_controller[nr])
2532 SDL_GameControllerClose(sdl_joystick[nr]);
2534 SDL_JoystickClose(sdl_joystick[nr]);
2536 sdl_joystick[nr] = NULL;
2539 boolean SDLCheckJoystickOpened(int nr)
2541 if (nr < 0 || nr >= MAX_PLAYERS)
2544 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2547 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2549 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2550 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2551 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2552 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2554 if (nr < 0 || nr >= MAX_PLAYERS)
2560 // prevent (slightly jittering, but centered) axis A from resetting axis B
2561 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2562 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2565 sdl_js_axis[nr][axis_id] = axis_value;
2566 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2569 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2571 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2572 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2573 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2574 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2575 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2576 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2577 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2578 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2581 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2582 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2583 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2584 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2585 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2586 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2587 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2588 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2590 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2591 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2592 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2593 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2594 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2596 if (nr < 0 || nr >= MAX_PLAYERS)
2599 if (button_id == -1)
2602 sdl_js_button[nr][button_id] = button_state;
2605 void HandleJoystickEvent(Event *event)
2607 // when using joystick, disable overlay touch buttons
2608 runtime.uses_touch_device = FALSE;
2610 switch (event->type)
2612 case SDL_CONTROLLERDEVICEADDED:
2614 Debug("joystick", "SDL_CONTROLLERDEVICEADDED: device %d added",
2615 event->cdevice.which);
2620 case SDL_CONTROLLERDEVICEREMOVED:
2622 Debug("joystick", "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2623 event->cdevice.which);
2628 case SDL_CONTROLLERAXISMOTION:
2630 Debug("joystick", "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2631 event->caxis.which, event->caxis.axis, event->caxis.value);
2633 setJoystickAxis(event->caxis.which,
2635 event->caxis.value);
2638 case SDL_CONTROLLERBUTTONDOWN:
2640 Debug("joystick", "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2641 event->cbutton.which, event->cbutton.button);
2643 setJoystickButton(event->cbutton.which,
2644 event->cbutton.button,
2648 case SDL_CONTROLLERBUTTONUP:
2650 Debug("joystick", "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2651 event->cbutton.which, event->cbutton.button);
2653 setJoystickButton(event->cbutton.which,
2654 event->cbutton.button,
2658 case SDL_JOYAXISMOTION:
2659 if (sdl_is_controller[event->jaxis.which])
2663 Debug("joystick", "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2664 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2666 if (event->jaxis.axis < 4)
2667 setJoystickAxis(event->jaxis.which,
2669 event->jaxis.value);
2672 case SDL_JOYBUTTONDOWN:
2673 if (sdl_is_controller[event->jaxis.which])
2677 Debug("joystick", "SDL_JOYBUTTONDOWN: device %d, button %d",
2678 event->jbutton.which, event->jbutton.button);
2680 if (event->jbutton.button < 4)
2681 setJoystickButton(event->jbutton.which,
2682 event->jbutton.button,
2686 case SDL_JOYBUTTONUP:
2687 if (sdl_is_controller[event->jaxis.which])
2691 Debug("joystick", "SDL_JOYBUTTONUP: device %d, button %d",
2692 event->jbutton.which, event->jbutton.button);
2694 if (event->jbutton.button < 4)
2695 setJoystickButton(event->jbutton.which,
2696 event->jbutton.button,
2705 void SDLInitJoysticks(void)
2707 static boolean sdl_joystick_subsystem_initialized = FALSE;
2708 boolean print_warning = !sdl_joystick_subsystem_initialized;
2709 char *mappings_file_base = getPath2(options.conf_directory,
2710 GAMECONTROLLER_BASENAME);
2711 char *mappings_file_user = getPath2(getMainUserGameDataDir(),
2712 GAMECONTROLLER_BASENAME);
2716 if (!sdl_joystick_subsystem_initialized)
2718 sdl_joystick_subsystem_initialized = TRUE;
2720 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2722 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2723 Fail("SDL_Init() failed: %s", SDL_GetError());
2725 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2727 // the included game controller base mappings should always be found
2728 if (num_mappings == -1)
2729 Warn("no game controller base mappings found");
2732 Debug("joystick", "%d game controller base mapping(s) added",
2736 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2739 // the personal game controller user mappings may or may not be found
2740 if (num_mappings == -1)
2741 Warn("no game controller user mappings found");
2743 Debug("joystick", , "%d game controller user mapping(s) added",
2746 Debug("joystick", "%d joystick(s) found:", SDL_NumJoysticks());
2749 checked_free(mappings_file_base);
2750 checked_free(mappings_file_user);
2753 for (i = 0; i < SDL_NumJoysticks(); i++)
2755 const char *name, *type;
2757 if (SDL_IsGameController(i))
2759 name = SDL_GameControllerNameForIndex(i);
2760 type = "game controller";
2764 name = SDL_JoystickNameForIndex(i);
2768 Debug("joystick", "- joystick %d (%s): '%s'",
2769 i, type, (name ? name : "(Unknown)"));
2774 // assign joysticks from configured to connected joystick for all players
2775 for (i = 0; i < MAX_PLAYERS; i++)
2777 // get configured joystick for this player
2778 char *device_name = setup.input[i].joy.device_name;
2779 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2781 if (joystick_nr >= SDL_NumJoysticks())
2783 if (setup.input[i].use_joystick && print_warning)
2784 Warn("cannot find joystick %d", joystick_nr);
2789 // store configured joystick number for each player
2790 joystick.nr[i] = joystick_nr;
2793 // now open all connected joysticks (regardless if configured or not)
2794 for (i = 0; i < SDL_NumJoysticks(); i++)
2796 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2797 if (SDLCheckJoystickOpened(i))
2798 SDLCloseJoystick(i);
2800 if (SDLOpenJoystick(i))
2801 joystick.status = JOYSTICK_ACTIVATED;
2802 else if (print_warning)
2803 Warn("cannot open joystick %d", i);
2806 SDLClearJoystickState();
2809 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2811 if (nr < 0 || nr >= MAX_PLAYERS)
2815 *x = sdl_js_axis[nr][0];
2817 *y = sdl_js_axis[nr][1];
2820 *b1 = sdl_js_button[nr][0];
2822 *b2 = sdl_js_button[nr][1];
2828 // ============================================================================
2829 // touch input overlay functions
2830 // ============================================================================
2832 #if defined(USE_TOUCH_INPUT_OVERLAY)
2833 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2836 int grid_xsize = overlay.grid_xsize;
2837 int grid_ysize = overlay.grid_ysize;
2840 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2841 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2843 for (x = 0; x < grid_xsize; x++)
2845 rect.x = (x + 0) * video.screen_width / grid_xsize;
2846 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2848 for (y = 0; y < grid_ysize; y++)
2850 rect.y = (y + 0) * video.screen_height / grid_ysize;
2851 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2853 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2854 SDL_RenderDrawRect(sdl_renderer, &rect);
2858 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2861 static void RenderFillRectangle(int x, int y, int width, int height)
2863 SDL_Rect rect = { x, y, width, height };
2865 SDL_RenderFillRect(sdl_renderer, &rect);
2868 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2870 static int alpha_direction = 0;
2871 static int alpha_highlight = 0;
2872 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2873 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2875 int grid_xsize = overlay.grid_xsize;
2876 int grid_ysize = overlay.grid_ysize;
2879 if (alpha == alpha_max)
2881 if (alpha_direction < 0)
2883 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2885 if (alpha_highlight == 0)
2886 alpha_direction = 1;
2890 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2892 if (alpha_highlight == alpha_max)
2893 alpha_direction = -1;
2898 alpha_direction = 1;
2899 alpha_highlight = alpha;
2902 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2904 for (x = 0; x < grid_xsize; x++)
2906 for (y = 0; y < grid_ysize; y++)
2908 int grid_button = overlay.grid_button[x][y];
2909 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2910 int alpha_draw = alpha;
2911 int outline_border = MV_NONE;
2912 int border_size = 2;
2913 boolean draw_outlined = setup.touch.draw_outlined;
2914 boolean draw_pressed = setup.touch.draw_pressed;
2916 if (grid_button == CHAR_GRID_BUTTON_NONE)
2919 if (grid_button == overlay.grid_button_highlight)
2921 draw_outlined = FALSE;
2922 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2925 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2928 draw_outlined = FALSE;
2930 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2933 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2935 rect.x = (x + 0) * video.screen_width / grid_xsize;
2936 rect.y = (y + 0) * video.screen_height / grid_ysize;
2937 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2938 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2940 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2942 rect.x += border_size;
2943 rect.w -= border_size;
2945 outline_border |= MV_LEFT;
2948 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2950 rect.w -= border_size;
2952 outline_border |= MV_RIGHT;
2955 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2957 rect.y += border_size;
2958 rect.h -= border_size;
2960 outline_border |= MV_UP;
2963 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2965 rect.h -= border_size;
2967 outline_border |= MV_DOWN;
2972 int rect_x = rect.x +
2973 (outline_border & MV_LEFT ? border_size : 0);
2974 int rect_w = rect.w -
2975 (outline_border & MV_LEFT ? border_size : 0) -
2976 (outline_border & MV_RIGHT ? border_size : 0);
2978 if (outline_border & MV_LEFT)
2979 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2981 if (outline_border & MV_RIGHT)
2982 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2983 border_size, rect.h);
2985 if (outline_border & MV_UP)
2986 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2988 if (outline_border & MV_DOWN)
2989 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
2990 rect_w, border_size);
2994 SDL_RenderFillRect(sdl_renderer, &rect);
2999 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3002 static void DrawTouchInputOverlay(void)
3004 static boolean deactivated = TRUE;
3005 static boolean show_grid = FALSE;
3006 static int alpha = 0;
3007 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3008 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3009 boolean active = (overlay.enabled && overlay.active);
3011 if (!active && deactivated)
3016 if (alpha < alpha_max)
3017 alpha = MIN(alpha + alpha_step, alpha_max);
3019 deactivated = FALSE;
3023 alpha = MAX(0, alpha - alpha_step);
3029 if (overlay.show_grid)
3031 else if (deactivated)
3035 DrawTouchInputOverlay_ShowGrid(alpha);
3037 DrawTouchInputOverlay_ShowGridButtons(alpha);
3040 static void DrawTouchGadgetsOverlay(void)
3042 DrawGadgets_OverlayTouchButtons();