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 DelayCounter update_screen_delay = { 50 }; // (milliseconds)
73 SDL_Surface *screen = backbuffer->surface;
75 if (limit_screen_updates &&
76 !DelayReached(&update_screen_delay))
79 LimitScreenUpdates(FALSE);
83 static int LastFrameCounter = 0;
84 boolean changed = (FrameCounter != LastFrameCounter);
86 Debug("internal:frame", "FrameCounter == %d [%s]", FrameCounter,
87 (changed ? "-" : "SAME FRAME UPDATED"));
89 LastFrameCounter = FrameCounter;
98 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
99 gfx.final_screen_bitmap != NULL) // may not be initialized yet
101 // draw global animations using bitmaps instead of using textures
102 // to prevent texture scaling artefacts (this is potentially slower)
104 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
105 gfx.win_xsize, gfx.win_ysize, 0, 0);
107 FinalizeScreen(DRAW_TO_SCREEN);
109 screen = gfx.final_screen_bitmap->surface;
111 // force full window redraw
115 SDL_Texture *sdl_texture = sdl_texture_stream;
117 // deactivate use of target texture if render targets are not supported
118 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
119 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
120 sdl_texture_target == NULL)
121 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
123 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
124 sdl_texture = sdl_texture_target;
128 int bytes_x = screen->pitch / video.width;
129 int bytes_y = screen->pitch;
131 SDL_UpdateTexture(sdl_texture, rect,
132 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
137 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
140 int xoff = video.screen_xoffset;
141 int yoff = video.screen_yoffset;
142 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
143 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
144 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
146 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
147 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
148 dst_rect2 = &dst_rect_screen;
150 dst_rect1 = &dst_rect_screen;
152 #if defined(HAS_SCREEN_KEYBOARD)
153 SDL_Rect src_rect_up = { 0, 0, video.width, video.height };
154 SDL_Rect dst_rect_up = dst_rect_screen;
156 if (video.shifted_up || video.shifted_up_delay.count)
158 int time_current = SDL_GetTicks();
159 int pos = video.shifted_up_pos;
160 int pos_last = video.shifted_up_pos_last;
162 if (!DelayReachedExt(&video.shifted_up_delay, time_current))
164 int delay_count = time_current - video.shifted_up_delay.count;
165 int delay_value = video.shifted_up_delay.value;
167 pos = pos_last + (pos - pos_last) * delay_count / delay_value;
171 video.shifted_up_pos_last = pos;
172 video.shifted_up_delay.count = 0;
176 src_rect_up.h = video.height - pos;
177 dst_rect_up.h = video.height - pos;
179 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
180 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
182 src_rect2 = &src_rect_up;
183 dst_rect2 = &dst_rect_up;
187 src_rect1 = &src_rect_up;
188 dst_rect1 = &dst_rect_up;
193 // clear render target buffer
194 SDL_RenderClear(sdl_renderer);
196 // set renderer to use target texture for rendering
197 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
198 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
199 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
201 // copy backbuffer texture to render target buffer
202 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
203 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
205 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
206 FinalizeScreen(DRAW_TO_SCREEN);
208 // when using target texture, copy it to screen buffer
209 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
210 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
212 SDL_SetRenderTarget(sdl_renderer, NULL);
213 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
216 #if defined(USE_TOUCH_INPUT_OVERLAY)
217 // draw overlay graphics for touch device input, if needed
218 DrawTouchInputOverlay();
220 // draw overlay gadgets for touch device input, if needed
221 DrawTouchGadgetsOverlay();
224 // global synchronization point of the game to align video frame delay
225 if (with_frame_delay)
226 WaitUntilDelayReached(&video.frame_delay);
228 video.frame_counter++;
230 // show render target buffer on screen
231 SDL_RenderPresent(sdl_renderer);
234 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
236 PumpEvents(); // execute event filter actions while waiting
238 UpdateScreenExt(rect, TRUE);
241 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
243 UpdateScreenExt(rect, FALSE);
246 void Delay_WithScreenUpdates(unsigned int delay)
248 unsigned int time_current = SDL_GetTicks();
249 unsigned int time_delayed = time_current + delay;
251 while (time_current < time_delayed)
253 // updating the screen contains waiting for frame delay (non-busy)
254 UpdateScreen_WithFrameDelay(NULL);
256 time_current = SDL_GetTicks();
260 static void SDLSetWindowIcon(char *basename)
262 // (setting the window icon on Mac OS X would replace the high-quality
263 // dock icon with the currently smaller (and uglier) icon from file)
265 #if !defined(PLATFORM_MAC)
266 char *filename = getCustomImageFilename(basename);
267 SDL_Surface *surface;
269 if (filename == NULL)
271 Warn("SDLSetWindowIcon(): cannot find file '%s'", basename);
276 if ((surface = IMG_Load(filename)) == NULL)
278 Warn("IMG_Load('%s') failed: %s", basename, SDL_GetError());
283 // set transparent color
284 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
285 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
287 SDL_SetWindowIcon(sdl_window, surface);
291 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
292 SDL_PixelFormat *format2)
294 return (format1->format == format2->format &&
295 format1->BitsPerPixel == format2->BitsPerPixel &&
296 format1->BytesPerPixel == format2->BytesPerPixel &&
297 format1->Rmask == format2->Rmask &&
298 format1->Gmask == format2->Gmask &&
299 format1->Bmask == format2->Bmask);
302 static void SDLCopyColorKey(SDL_Surface *src_surface, SDL_Surface *dst_surface)
307 // check if source surface has a color key
308 if (SDL_GetColorKey(src_surface, &color_key) == 0)
310 // get RGB values of color key of source surface
311 SDL_GetRGB(color_key, src_surface->format, &r, &g, &b);
313 // get color key from RGB values in destination surface format
314 color_key = SDL_MapRGB(dst_surface->format, r, g, b);
316 // set color key in destination surface
317 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL, color_key);
321 // unset color key in destination surface
322 SDL_SetColorKey(dst_surface, UNSET_TRANSPARENT_PIXEL, 0);
326 static boolean SDLHasColorKey(SDL_Surface *surface)
330 return (SDL_GetColorKey(surface, &color_key) == 0);
333 static boolean SDLHasAlpha(SDL_Surface *surface)
335 SDL_BlendMode blend_mode;
337 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
340 return (blend_mode == SDL_BLENDMODE_BLEND);
343 void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
345 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
347 SDL_SetSurfaceBlendMode(surface, blend_mode);
348 SDL_SetSurfaceAlphaMod(surface, alpha);
351 const char *SDLGetRendererName(void)
353 static SDL_RendererInfo renderer_info;
355 SDL_GetRendererInfo(sdl_renderer, &renderer_info);
357 return renderer_info.name;
360 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
362 SDL_PixelFormat format;
363 SDL_Surface *new_surface;
368 if (backbuffer && backbuffer->surface)
370 format = *backbuffer->surface->format;
371 format.Amask = surface->format->Amask; // keep alpha channel
375 format = *surface->format;
378 new_surface = SDL_ConvertSurface(surface, &format, 0);
380 if (new_surface == NULL)
381 Fail("SDL_ConvertSurface() failed: %s", SDL_GetError());
383 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
384 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
385 SDLCopyColorKey(surface, new_surface);
390 boolean SDLSetNativeSurface(SDL_Surface **surface)
392 SDL_Surface *new_surface;
394 if (surface == NULL ||
396 backbuffer == NULL ||
397 backbuffer->surface == NULL)
400 // if pixel format already optimized for destination surface, do nothing
401 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
404 new_surface = SDLGetNativeSurface(*surface);
406 SDL_FreeSurface(*surface);
408 *surface = new_surface;
413 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
415 if (program.headless)
418 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
421 Fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
426 void SDLCreateBitmapTextures(Bitmap *bitmap)
432 SDL_DestroyTexture(bitmap->texture);
433 if (bitmap->texture_masked)
434 SDL_DestroyTexture(bitmap->texture_masked);
436 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
437 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
440 void SDLFreeBitmapTextures(Bitmap *bitmap)
446 SDL_DestroyTexture(bitmap->texture);
447 if (bitmap->texture_masked)
448 SDL_DestroyTexture(bitmap->texture_masked);
450 bitmap->texture = NULL;
451 bitmap->texture_masked = NULL;
454 void SDLInitVideoDisplay(void)
456 // set hint to select render driver as specified in setup config file
457 if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
458 SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
460 // initialize SDL video
461 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
462 Fail("SDL_InitSubSystem() failed: %s", SDL_GetError());
464 // set default SDL depth
465 video.default_depth = 32; // (how to determine video depth in SDL2?)
467 // Code used with SDL 1.2:
468 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
471 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
473 if (program.headless)
476 video.window_scaling_percent = setup.window_scaling_percent;
477 video.window_scaling_quality = setup.window_scaling_quality;
479 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
481 // SDL 2.0: support for (desktop) fullscreen mode available
482 video.fullscreen_available = TRUE;
484 // open SDL video output device (window or fullscreen mode)
485 if (!SDLSetVideoMode(fullscreen))
486 Fail("setting video mode failed");
488 // !!! SDL2 can only set the window icon if the window already exists !!!
490 SDLSetWindowIcon(program.icon_filename);
492 // set window and icon title
496 static void SDLInitVideoBuffer_DrawBuffer(void)
498 /* SDL cannot directly draw to the visible video framebuffer like X11,
499 but always uses a backbuffer, which is then blitted to the visible
500 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
501 visible video framebuffer with 'SDL_Flip', if the hardware supports
502 this). Therefore do not use an additional backbuffer for drawing, but
503 use a symbolic buffer (distinguishable from the SDL backbuffer) called
504 'window', which indicates that the SDL backbuffer should be updated to
505 the visible video framebuffer when attempting to blit to it.
507 For convenience, it seems to be a good idea to create this symbolic
508 buffer 'window' at the same size as the SDL backbuffer. Although it
509 should never be drawn to directly, it would do no harm nevertheless. */
511 // create additional (symbolic) buffer for double-buffering
512 ReCreateBitmap(&window, video.width, video.height);
514 // create dummy drawing buffer for headless mode, if needed
515 if (program.headless)
516 ReCreateBitmap(&backbuffer, video.width, video.height);
519 void SDLInitVideoBuffer(boolean fullscreen)
521 SDLInitVideoBuffer_VideoBuffer(fullscreen);
522 SDLInitVideoBuffer_DrawBuffer();
525 static boolean SDLCreateScreen(boolean fullscreen)
527 SDL_Surface *new_surface = NULL;
529 int surface_flags_window = SURFACE_FLAGS;
530 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
533 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
535 video.vsync_mode = VSYNC_MODE_OFF;
537 if (!strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF))
539 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
540 video.vsync_mode = VSYNC_MODE_NORMAL;
543 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
544 _without_ enabling 2D/3D acceleration and/or guest additions installed,
545 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
546 it will try to use accelerated graphics and apparently fails miserably) */
547 int renderer_flags = SDL_RENDERER_SOFTWARE;
550 int width = video.width;
551 int height = video.height;
552 int screen_width = video.screen_width;
553 int screen_height = video.screen_height;
554 int surface_flags = (fullscreen ? surface_flags_fullscreen :
555 surface_flags_window);
556 int display_nr = options.display_nr;
558 // default window size is unscaled
559 video.window_width = screen_width;
560 video.window_height = screen_height;
562 // store if initial screen mode is fullscreen mode when changing screen size
563 video.fullscreen_initial = fullscreen;
565 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
567 video.window_width = window_scaling_factor * screen_width;
568 video.window_height = window_scaling_factor * screen_height;
570 if (sdl_texture_stream)
572 SDL_DestroyTexture(sdl_texture_stream);
573 sdl_texture_stream = NULL;
576 if (sdl_texture_target)
578 SDL_DestroyTexture(sdl_texture_target);
579 sdl_texture_target = NULL;
582 if (!(fullscreen && fullscreen_enabled))
586 SDL_DestroyRenderer(sdl_renderer);
592 SDL_SetWindowSize(sdl_window, video.window_width, video.window_height);
596 if (sdl_window == NULL)
597 sdl_window = SDL_CreateWindow(program.window_title,
598 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
599 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
604 if (sdl_window != NULL)
606 if (sdl_renderer == NULL)
607 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
609 if (sdl_renderer != NULL)
611 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
612 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
614 // required for setting adaptive vsync when using OpenGL renderer
615 SDLSetScreenVsyncMode(setup.vsync_mode);
617 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
618 SDL_PIXELFORMAT_ARGB8888,
619 SDL_TEXTUREACCESS_STREAMING,
622 if (SDL_RenderTargetSupported(sdl_renderer))
623 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
624 SDL_PIXELFORMAT_ARGB8888,
625 SDL_TEXTUREACCESS_TARGET,
628 if (sdl_texture_stream != NULL)
630 // use SDL default values for RGB masks and no alpha channel
631 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
633 if (new_surface == NULL)
634 Warn("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
638 Warn("SDL_CreateTexture() failed: %s", SDL_GetError());
643 Warn("SDL_CreateRenderer() failed: %s", SDL_GetError());
648 Warn("SDL_CreateWindow() failed: %s", SDL_GetError());
651 SDLSetScreenProperties();
653 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
654 if (new_surface != NULL)
655 fullscreen_enabled = fullscreen;
657 if (backbuffer == NULL)
658 backbuffer = CreateBitmapStruct();
660 backbuffer->width = video.width;
661 backbuffer->height = video.height;
663 if (backbuffer->surface)
664 SDL_FreeSurface(backbuffer->surface);
666 backbuffer->surface = new_surface;
668 return (new_surface != NULL);
671 boolean SDLSetVideoMode(boolean fullscreen)
673 boolean success = FALSE;
677 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
679 // switch display to fullscreen mode, if available
680 success = SDLCreateScreen(TRUE);
684 // switching display to fullscreen mode failed -- do not try it again
685 video.fullscreen_available = FALSE;
689 video.fullscreen_enabled = TRUE;
693 if ((!fullscreen && video.fullscreen_enabled) || !success)
695 // switch display to window mode
696 success = SDLCreateScreen(FALSE);
700 // switching display to window mode failed -- should not happen
704 video.fullscreen_enabled = FALSE;
705 video.window_scaling_percent = setup.window_scaling_percent;
706 video.window_scaling_quality = setup.window_scaling_quality;
708 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
712 SDLRedrawWindow(); // map window
717 void SDLSetWindowTitle(void)
719 if (sdl_window == NULL)
722 SDL_SetWindowTitle(sdl_window, program.window_title);
725 void SDLSetWindowScaling(int window_scaling_percent)
727 if (sdl_window == NULL)
730 float window_scaling_factor = (float)window_scaling_percent / 100;
731 int new_window_width = (int)(window_scaling_factor * video.screen_width);
732 int new_window_height = (int)(window_scaling_factor * video.screen_height);
734 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
736 video.window_scaling_percent = window_scaling_percent;
737 video.window_width = new_window_width;
738 video.window_height = new_window_height;
743 void SDLSetWindowScalingQuality(char *window_scaling_quality)
745 SDL_Texture *new_texture;
747 if (sdl_texture_stream == NULL)
750 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
752 new_texture = SDL_CreateTexture(sdl_renderer,
753 SDL_PIXELFORMAT_ARGB8888,
754 SDL_TEXTUREACCESS_STREAMING,
755 video.width, video.height);
757 if (new_texture != NULL)
759 SDL_DestroyTexture(sdl_texture_stream);
761 sdl_texture_stream = new_texture;
764 if (SDL_RenderTargetSupported(sdl_renderer))
765 new_texture = SDL_CreateTexture(sdl_renderer,
766 SDL_PIXELFORMAT_ARGB8888,
767 SDL_TEXTUREACCESS_TARGET,
768 video.width, video.height);
772 if (new_texture != NULL)
774 SDL_DestroyTexture(sdl_texture_target);
776 sdl_texture_target = new_texture;
781 video.window_scaling_quality = window_scaling_quality;
784 void SDLSetWindowFullscreen(boolean fullscreen)
786 if (sdl_window == NULL)
789 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
791 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
792 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
794 // if screen size was changed in fullscreen mode, correct desktop window size
795 if (!fullscreen && video.fullscreen_initial)
797 SDLSetWindowScaling(setup.window_scaling_percent);
798 SDL_SetWindowPosition(sdl_window,
799 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr),
800 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr));
802 video.fullscreen_initial = FALSE;
806 void SDLSetDisplaySize(void)
808 if (sdl_renderer != NULL)
812 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
814 video.display_width = w;
815 video.display_height = h;
818 Debug("video", "SDL renderer size: %d x %d",
819 video.display_width, video.display_height);
824 SDL_Rect display_bounds;
826 SDL_GetDisplayBounds(0, &display_bounds);
828 video.display_width = display_bounds.w;
829 video.display_height = display_bounds.h;
832 Debug("video", "SDL display size: %d x %d",
833 video.display_width, video.display_height);
838 void SDLSetScreenSizeAndOffsets(int width, int height)
840 // set default video screen size and offsets
841 video.screen_width = width;
842 video.screen_height = height;
843 video.screen_xoffset = 0;
844 video.screen_yoffset = 0;
846 #if defined(USE_COMPLETE_DISPLAY)
847 float ratio_video = (float) width / height;
848 float ratio_display = (float) video.display_width / video.display_height;
850 if (ratio_video != ratio_display)
852 // adjust drawable screen size to cover the whole device display
854 if (ratio_video < ratio_display)
855 video.screen_width *= ratio_display / ratio_video;
857 video.screen_height *= ratio_video / ratio_display;
859 video.screen_xoffset = (video.screen_width - width) / 2;
860 video.screen_yoffset = (video.screen_height - height) / 2;
863 Debug("video", "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
865 video.screen_width, video.screen_height,
866 ratio_video, ratio_display);
872 void SDLSetScreenSizeForRenderer(int width, int height)
874 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
877 void SDLSetScreenProperties(void)
880 SDLSetScreenSizeAndOffsets(video.width, video.height);
881 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
883 SetOverlayGridSizeAndButtons();
886 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
888 video.screen_rendering_mode =
889 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
890 SPECIAL_RENDERING_BITMAP :
891 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
892 SPECIAL_RENDERING_TARGET:
893 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
894 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
897 void SDLSetScreenVsyncMode(char *vsync_mode)
899 // changing vsync mode without re-creating renderer only supported by OpenGL
900 if (!strPrefixLower((char *)SDLGetRendererName(), "opengl"))
903 int interval = VSYNC_MODE_STR_TO_INT(vsync_mode);
904 int result = SDL_GL_SetSwapInterval(interval);
906 // if adaptive vsync requested, but not supported, retry with normal vsync
907 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
909 interval = VSYNC_MODE_NORMAL;
911 result = SDL_GL_SetSwapInterval(interval);
915 interval = VSYNC_MODE_OFF;
917 video.vsync_mode = interval;
920 void SDLRedrawWindow(void)
922 UpdateScreen_WithoutFrameDelay(NULL);
925 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
928 if (program.headless)
931 SDL_Surface *surface =
932 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
935 Fail("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
937 SDLSetNativeSurface(&surface);
939 bitmap->surface = surface;
942 void SDLFreeBitmapPointers(Bitmap *bitmap)
945 SDL_FreeSurface(bitmap->surface);
946 if (bitmap->surface_masked)
947 SDL_FreeSurface(bitmap->surface_masked);
949 bitmap->surface = NULL;
950 bitmap->surface_masked = NULL;
953 SDL_DestroyTexture(bitmap->texture);
954 if (bitmap->texture_masked)
955 SDL_DestroyTexture(bitmap->texture_masked);
957 bitmap->texture = NULL;
958 bitmap->texture_masked = NULL;
961 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
962 int src_x, int src_y, int width, int height,
963 int dst_x, int dst_y, int mask_mode)
965 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
966 SDL_Rect src_rect, dst_rect;
978 // if (src_bitmap != backbuffer || dst_bitmap != window)
979 if (!(src_bitmap == backbuffer && dst_bitmap == window))
980 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
981 src_bitmap->surface_masked : src_bitmap->surface),
982 &src_rect, real_dst_bitmap->surface, &dst_rect);
984 if (dst_bitmap == window)
985 UpdateScreen_WithFrameDelay(&dst_rect);
988 void SDLBlitTexture(Bitmap *bitmap,
989 int src_x, int src_y, int width, int height,
990 int dst_x, int dst_y, int mask_mode)
992 SDL_Texture *texture;
997 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1005 src_rect.h = height;
1010 dst_rect.h = height;
1012 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1015 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1018 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1026 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1028 if (dst_bitmap == window)
1029 UpdateScreen_WithFrameDelay(&rect);
1032 void PrepareFadeBitmap(int draw_target)
1034 Bitmap *fade_bitmap =
1035 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1036 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1038 if (fade_bitmap == NULL)
1041 // copy backbuffer to fading buffer
1042 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1044 // add border and animations to fading buffer
1045 FinalizeScreen(draw_target);
1048 void SDLFadeRectangle(int x, int y, int width, int height,
1049 int fade_mode, int fade_delay, int post_delay,
1050 void (*draw_border_function)(void))
1052 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1053 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1054 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1055 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1056 SDL_Surface *surface_screen = backbuffer->surface;
1057 SDL_Rect src_rect, dst_rect;
1059 int src_x = x, src_y = y;
1060 int dst_x = x, dst_y = y;
1061 unsigned int time_last, time_current;
1063 // store function for drawing global masked border
1064 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1066 // deactivate drawing of global border while fading, if needed
1067 if (draw_border_function == NULL)
1068 gfx.draw_global_border_function = NULL;
1073 src_rect.h = height;
1077 dst_rect.w = width; // (ignored)
1078 dst_rect.h = height; // (ignored)
1080 dst_rect2 = dst_rect;
1082 // before fading in, store backbuffer (without animation graphics)
1083 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1084 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1086 // copy source and target surfaces to temporary surfaces for fading
1087 if (fade_mode & FADE_TYPE_TRANSFORM)
1089 // (source and target fading buffer already prepared)
1091 else if (fade_mode & FADE_TYPE_FADE_IN)
1093 // (target fading buffer already prepared)
1094 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1096 else // FADE_TYPE_FADE_OUT
1098 // (source fading buffer already prepared)
1099 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1102 time_current = SDL_GetTicks();
1104 if (fade_delay <= 0)
1106 // immediately draw final target frame without delay
1107 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1111 // when fading without delay, also skip post delay
1115 if (fade_mode == FADE_MODE_MELT)
1117 boolean done = FALSE;
1118 int melt_pixels = 2;
1119 int melt_columns = width / melt_pixels;
1120 int ypos[melt_columns];
1121 int max_steps = height / 8 + 32;
1126 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1128 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1130 ypos[0] = -GetSimpleRandom(16);
1132 for (i = 1 ; i < melt_columns; i++)
1134 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1136 ypos[i] = ypos[i - 1] + r;
1149 time_last = time_current;
1150 time_current = SDL_GetTicks();
1151 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1152 steps_final = MIN(MAX(0, steps), max_steps);
1156 done = (steps_done >= steps_final);
1158 for (i = 0 ; i < melt_columns; i++)
1166 else if (ypos[i] < height)
1171 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1173 if (ypos[i] + dy >= height)
1174 dy = height - ypos[i];
1176 // copy part of (appearing) target surface to upper area
1177 src_rect.x = src_x + i * melt_pixels;
1178 // src_rect.y = src_y + ypos[i];
1180 src_rect.w = melt_pixels;
1182 src_rect.h = ypos[i] + dy;
1184 dst_rect.x = dst_x + i * melt_pixels;
1185 // dst_rect.y = dst_y + ypos[i];
1188 if (steps_done >= steps_final)
1189 SDL_BlitSurface(surface_target, &src_rect,
1190 surface_screen, &dst_rect);
1194 // copy part of (disappearing) source surface to lower area
1195 src_rect.x = src_x + i * melt_pixels;
1197 src_rect.w = melt_pixels;
1198 src_rect.h = height - ypos[i];
1200 dst_rect.x = dst_x + i * melt_pixels;
1201 dst_rect.y = dst_y + ypos[i];
1203 if (steps_done >= steps_final)
1204 SDL_BlitSurface(surface_source, &src_rect,
1205 surface_screen, &dst_rect);
1211 src_rect.x = src_x + i * melt_pixels;
1213 src_rect.w = melt_pixels;
1214 src_rect.h = height;
1216 dst_rect.x = dst_x + i * melt_pixels;
1219 if (steps_done >= steps_final)
1220 SDL_BlitSurface(surface_target, &src_rect,
1221 surface_screen, &dst_rect);
1225 if (steps_done >= steps_final)
1227 if (draw_border_function != NULL)
1228 draw_border_function();
1230 UpdateScreen_WithFrameDelay(&dst_rect2);
1232 if (PendingEscapeKeyEvent())
1237 else if (fade_mode == FADE_MODE_CURTAIN)
1241 int xx_size = width / 2;
1243 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1245 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1247 for (xx = 0; xx < xx_size;)
1249 time_last = time_current;
1250 time_current = SDL_GetTicks();
1251 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1252 xx_final = MIN(MAX(0, xx), xx_size);
1257 src_rect.h = height;
1262 // draw new (target) image to screen buffer
1263 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1265 if (xx_final < xx_size)
1267 src_rect.w = xx_size - xx_final;
1268 src_rect.h = height;
1270 // draw old (source) image to screen buffer (left side)
1272 src_rect.x = src_x + xx_final;
1275 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1277 // draw old (source) image to screen buffer (right side)
1279 src_rect.x = src_x + xx_size;
1280 dst_rect.x = dst_x + xx_size + xx_final;
1282 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1285 if (draw_border_function != NULL)
1286 draw_border_function();
1288 // only update the region of the screen that is affected from fading
1289 UpdateScreen_WithFrameDelay(&dst_rect2);
1291 if (PendingEscapeKeyEvent())
1295 else // fading in, fading out or cross-fading
1300 for (alpha = 0.0; alpha < 255.0;)
1302 time_last = time_current;
1303 time_current = SDL_GetTicks();
1304 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1305 alpha_final = MIN(MAX(0, alpha), 255);
1307 // draw existing (source) image to screen buffer
1308 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1310 // draw new (target) image to screen buffer using alpha blending
1311 SDLSetAlpha(surface_target, TRUE, alpha_final);
1312 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1314 if (draw_border_function != NULL)
1315 draw_border_function();
1317 // only update the region of the screen that is affected from fading
1318 UpdateScreen_WithFrameDelay(&dst_rect);
1320 if (PendingEscapeKeyEvent())
1326 Delay_WithScreenUpdates(post_delay);
1328 // restore function for drawing global masked border
1329 gfx.draw_global_border_function = draw_global_border_function;
1331 // after fading in, restore backbuffer (without animation graphics)
1332 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1333 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1336 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1337 int to_x, int to_y, Uint32 color)
1339 SDL_Surface *surface = dst_bitmap->surface;
1343 swap_numbers(&from_x, &to_x);
1346 swap_numbers(&from_y, &to_y);
1350 rect.w = (to_x - from_x + 1);
1351 rect.h = (to_y - from_y + 1);
1353 SDL_FillRect(surface, &rect, color);
1356 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1357 int to_x, int to_y, Uint32 color)
1359 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1362 #if ENABLE_UNUSED_CODE
1363 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1364 int num_points, Uint32 color)
1369 for (i = 0; i < num_points - 1; i++)
1371 for (x = 0; x < line_width; x++)
1373 for (y = 0; y < line_width; y++)
1375 int dx = x - line_width / 2;
1376 int dy = y - line_width / 2;
1378 if ((x == 0 && y == 0) ||
1379 (x == 0 && y == line_width - 1) ||
1380 (x == line_width - 1 && y == 0) ||
1381 (x == line_width - 1 && y == line_width - 1))
1384 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1385 points[i+1].x + dx, points[i+1].y + dy, color);
1392 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1394 SDL_Surface *surface = src_bitmap->surface;
1396 switch (surface->format->BytesPerPixel)
1398 case 1: // assuming 8-bpp
1400 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1404 case 2: // probably 15-bpp or 16-bpp
1406 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1410 case 3: // slow 24-bpp mode; usually not used
1413 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1417 shift = surface->format->Rshift;
1418 color |= *(pix + shift / 8) >> shift;
1419 shift = surface->format->Gshift;
1420 color |= *(pix + shift / 8) >> shift;
1421 shift = surface->format->Bshift;
1422 color |= *(pix + shift / 8) >> shift;
1428 case 4: // probably 32-bpp
1430 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1439 // ============================================================================
1440 // The following functions were taken from the SGE library
1441 // (SDL Graphics Extension Library) by Anders Lindström
1442 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1443 // ============================================================================
1445 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1447 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1449 switch (surface->format->BytesPerPixel)
1454 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1460 // Probably 15-bpp or 16-bpp
1461 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1467 // Slow 24-bpp mode, usually not used
1471 // Gack - slow, but endian correct
1472 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1473 shift = surface->format->Rshift;
1474 *(pix+shift/8) = color>>shift;
1475 shift = surface->format->Gshift;
1476 *(pix+shift/8) = color>>shift;
1477 shift = surface->format->Bshift;
1478 *(pix+shift/8) = color>>shift;
1485 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1493 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1494 Uint8 R, Uint8 G, Uint8 B)
1496 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1499 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1501 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1504 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1506 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1509 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1514 // Gack - slow, but endian correct
1515 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1516 shift = surface->format->Rshift;
1517 *(pix+shift/8) = color>>shift;
1518 shift = surface->format->Gshift;
1519 *(pix+shift/8) = color>>shift;
1520 shift = surface->format->Bshift;
1521 *(pix+shift/8) = color>>shift;
1524 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1526 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1529 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1531 switch (dest->format->BytesPerPixel)
1534 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1538 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1542 _PutPixel24(dest,x,y,color);
1546 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1552 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1554 if (SDL_MUSTLOCK(surface))
1556 if (SDL_LockSurface(surface) < 0)
1562 _PutPixel(surface, x, y, color);
1564 if (SDL_MUSTLOCK(surface))
1566 SDL_UnlockSurface(surface);
1571 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1572 Uint8 r, Uint8 g, Uint8 b)
1574 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1577 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1579 if (y >= 0 && y <= dest->h - 1)
1581 switch (dest->format->BytesPerPixel)
1584 return y*dest->pitch;
1588 return y*dest->pitch/2;
1592 return y*dest->pitch;
1596 return y*dest->pitch/4;
1604 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1607 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1609 switch (surface->format->BytesPerPixel)
1614 *((Uint8 *)surface->pixels + ypitch + x) = color;
1620 // Probably 15-bpp or 16-bpp
1621 *((Uint16 *)surface->pixels + ypitch + x) = color;
1627 // Slow 24-bpp mode, usually not used
1631 // Gack - slow, but endian correct
1632 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1633 shift = surface->format->Rshift;
1634 *(pix+shift/8) = color>>shift;
1635 shift = surface->format->Gshift;
1636 *(pix+shift/8) = color>>shift;
1637 shift = surface->format->Bshift;
1638 *(pix+shift/8) = color>>shift;
1645 *((Uint32 *)surface->pixels + ypitch + x) = color;
1652 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1657 if (SDL_MUSTLOCK(Surface))
1659 if (SDL_LockSurface(Surface) < 0)
1673 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1677 if (x2 > Surface->w - 1)
1678 x2 = Surface->w - 1;
1685 SDL_FillRect(Surface, &l, Color);
1687 if (SDL_MUSTLOCK(Surface))
1689 SDL_UnlockSurface(Surface);
1693 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1694 Uint8 R, Uint8 G, Uint8 B)
1696 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1699 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1712 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1716 if (x2 > Surface->w - 1)
1717 x2 = Surface->w - 1;
1724 SDL_FillRect(Surface, &l, Color);
1727 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1732 if (SDL_MUSTLOCK(Surface))
1734 if (SDL_LockSurface(Surface) < 0)
1748 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1752 if (y2 > Surface->h - 1)
1753 y2 = Surface->h - 1;
1760 SDL_FillRect(Surface, &l, Color);
1762 if (SDL_MUSTLOCK(Surface))
1764 SDL_UnlockSurface(Surface);
1768 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1769 Uint8 R, Uint8 G, Uint8 B)
1771 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1774 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1787 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1791 if (y2 > Surface->h - 1)
1792 y2 = Surface->h - 1;
1799 SDL_FillRect(Surface, &l, Color);
1803 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1804 Sint16 x2, Sint16 y2, Uint32 Color,
1805 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1808 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1813 sdx = (dx < 0) ? -1 : 1;
1814 sdy = (dy < 0) ? -1 : 1;
1826 for (x = 0; x < dx; x++)
1828 Callback(Surface, px, py, Color);
1842 for (y = 0; y < dy; y++)
1844 Callback(Surface, px, py, Color);
1859 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1860 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1861 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1864 sge_DoLine(Surface, X1, Y1, X2, Y2,
1865 SDL_MapRGB(Surface->format, R, G, B), Callback);
1869 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1872 if (SDL_MUSTLOCK(Surface))
1874 if (SDL_LockSurface(Surface) < 0)
1879 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1881 // unlock the display
1882 if (SDL_MUSTLOCK(Surface))
1884 SDL_UnlockSurface(Surface);
1889 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1890 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1892 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1896 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1898 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1902 // ----------------------------------------------------------------------------
1903 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1904 // ----------------------------------------------------------------------------
1906 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1907 int src_x, int src_y, int width, int height,
1908 int dst_x, int dst_y)
1912 for (y = 0; y < height; y++)
1914 for (x = 0; x < width; x++)
1916 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1918 if (pixel != BLACK_PIXEL)
1919 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1925 // ============================================================================
1926 // The following functions were taken from the SDL_gfx library version 2.0.3
1927 // (Rotozoomer) by Andreas Schiffler
1928 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1929 // ============================================================================
1931 // ----------------------------------------------------------------------------
1934 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1935 // ----------------------------------------------------------------------------
1945 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1948 tColorRGBA *sp, *csp, *dp;
1952 sp = csp = (tColorRGBA *) src->pixels;
1953 dp = (tColorRGBA *) dst->pixels;
1954 dgap = dst->pitch - dst->w * 4;
1956 for (y = 0; y < dst->h; y++)
1960 for (x = 0; x < dst->w; x++)
1962 tColorRGBA *sp0 = sp;
1963 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1964 tColorRGBA *sp00 = &sp0[0];
1965 tColorRGBA *sp01 = &sp0[1];
1966 tColorRGBA *sp10 = &sp1[0];
1967 tColorRGBA *sp11 = &sp1[1];
1970 // create new color pixel from all four source color pixels
1971 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1972 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1973 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1974 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1979 // advance source pointers
1982 // advance destination pointer
1986 // advance source pointer
1987 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1989 // advance destination pointers
1990 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1996 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1998 int x, y, *sax, *say, *csax, *csay;
2000 tColorRGBA *sp, *csp, *csp0, *dp;
2003 // use specialized zoom function when scaling down to exactly half size
2004 if (src->w == 2 * dst->w &&
2005 src->h == 2 * dst->h)
2006 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2009 sx = (float) src->w / (float) dst->w;
2010 sy = (float) src->h / (float) dst->h;
2012 // allocate memory for row increments
2013 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2014 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2016 // precalculate row increments
2017 for (x = 0; x <= dst->w; x++)
2018 *csax++ = (int)(sx * x);
2020 for (y = 0; y <= dst->h; y++)
2021 *csay++ = (int)(sy * y);
2024 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2025 dp = (tColorRGBA *) dst->pixels;
2026 dgap = dst->pitch - dst->w * 4;
2029 for (y = 0; y < dst->h; y++)
2034 for (x = 0; x < dst->w; x++)
2039 // advance source pointers
2043 // advance destination pointer
2047 // advance source pointer
2049 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2051 // advance destination pointers
2052 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2061 // ----------------------------------------------------------------------------
2064 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2065 // ----------------------------------------------------------------------------
2067 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2069 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2070 Uint8 *sp, *dp, *csp;
2074 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2075 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2077 // allocate memory for row increments
2078 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2079 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2081 // precalculate row increments
2084 for (x = 0; x < dst->w; x++)
2087 *csax = (csx >> 16);
2094 for (y = 0; y < dst->h; y++)
2097 *csay = (csy >> 16);
2104 for (x = 0; x < dst->w; x++)
2112 for (y = 0; y < dst->h; y++)
2119 sp = csp = (Uint8 *) src->pixels;
2120 dp = (Uint8 *) dst->pixels;
2121 dgap = dst->pitch - dst->w;
2125 for (y = 0; y < dst->h; y++)
2129 for (x = 0; x < dst->w; x++)
2134 // advance source pointers
2138 // advance destination pointer
2142 // advance source pointer (for row)
2143 csp += ((*csay) * src->pitch);
2146 // advance destination pointers
2156 // ----------------------------------------------------------------------------
2159 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2160 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2161 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2162 // into a 32bit RGBA format on the fly.
2163 // ----------------------------------------------------------------------------
2165 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2167 SDL_Surface *zoom_src = NULL;
2168 SDL_Surface *zoom_dst = NULL;
2169 boolean is_converted = FALSE;
2176 // determine if source surface is 32 bit or 8 bit
2177 is_32bit = (src->format->BitsPerPixel == 32);
2179 if (is_32bit || src->format->BitsPerPixel == 8)
2181 // use source surface 'as is'
2186 // new source surface is 32 bit with a defined RGB ordering
2187 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2188 0x000000ff, 0x0000ff00, 0x00ff0000,
2189 (src->format->Amask ? 0xff000000 : 0));
2190 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2192 is_converted = TRUE;
2195 // allocate surface to completely contain the zoomed surface
2198 // target surface is 32 bit with source RGBA/ABGR ordering
2199 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2200 zoom_src->format->Rmask,
2201 zoom_src->format->Gmask,
2202 zoom_src->format->Bmask,
2203 zoom_src->format->Amask);
2207 // target surface is 8 bit
2208 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2212 // lock source surface
2213 SDL_LockSurface(zoom_src);
2215 // check which kind of surface we have
2218 // call the 32 bit transformation routine to do the zooming
2219 zoomSurfaceRGBA(zoom_src, zoom_dst);
2224 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2225 zoom_dst->format->palette->colors[i] =
2226 zoom_src->format->palette->colors[i];
2227 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2229 // call the 8 bit transformation routine to do the zooming
2230 zoomSurfaceY(zoom_src, zoom_dst);
2233 // unlock source surface
2234 SDL_UnlockSurface(zoom_src);
2236 // free temporary surface
2238 SDL_FreeSurface(zoom_src);
2240 // return destination surface
2244 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2246 SDL_Surface *new_surface;
2248 if (surface == NULL)
2251 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2252 Fail("SDLGetNativeSurface() failed");
2254 // remove alpha channel from native non-transparent surface, if defined
2255 SDLSetAlpha(new_surface, FALSE, 0);
2257 // remove transparent color from native non-transparent surface, if defined
2258 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2263 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2265 Bitmap *dst_bitmap = CreateBitmapStruct();
2266 SDL_Surface *src_surface = src_bitmap->surface_masked;
2267 SDL_Surface *dst_surface;
2269 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2270 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2272 dst_bitmap->width = dst_width;
2273 dst_bitmap->height = dst_height;
2275 // create zoomed temporary surface from source surface
2276 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2278 // create native format destination surface from zoomed temporary surface
2279 SDLSetNativeSurface(&dst_surface);
2281 // set color key for zoomed surface from source surface, if defined
2282 if (SDLHasColorKey(src_surface))
2283 SDLCopyColorKey(src_surface, dst_surface);
2285 // create native non-transparent surface for opaque blitting
2286 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2288 // set native transparent surface for masked blitting
2289 dst_bitmap->surface_masked = dst_surface;
2295 // ============================================================================
2296 // load image to bitmap
2297 // ============================================================================
2299 Bitmap *SDLLoadImage(char *filename)
2301 Bitmap *new_bitmap = CreateBitmapStruct();
2302 SDL_Surface *sdl_image_tmp;
2304 if (program.headless)
2306 // prevent sanity check warnings at later stage
2307 new_bitmap->width = new_bitmap->height = 1;
2312 print_timestamp_init("SDLLoadImage");
2314 print_timestamp_time(getBaseNamePtr(filename));
2316 // load image to temporary surface
2317 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2318 Fail("IMG_Load('%s') failed: %s", getBaseNamePtr(filename), SDL_GetError());
2320 print_timestamp_time("IMG_Load");
2322 UPDATE_BUSY_STATE();
2324 // create native non-transparent surface for current image
2325 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2326 Fail("SDLGetOpaqueSurface() failed");
2328 print_timestamp_time("SDLGetNativeSurface (opaque)");
2330 UPDATE_BUSY_STATE();
2332 // set black pixel to transparent if no alpha channel / transparent color
2333 if (!SDLHasAlpha(sdl_image_tmp) &&
2334 !SDLHasColorKey(sdl_image_tmp))
2335 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2336 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2338 // create native transparent surface for current image
2339 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2340 Fail("SDLGetNativeSurface() failed");
2342 print_timestamp_time("SDLGetNativeSurface (masked)");
2344 UPDATE_BUSY_STATE();
2346 // free temporary surface
2347 SDL_FreeSurface(sdl_image_tmp);
2349 new_bitmap->width = new_bitmap->surface->w;
2350 new_bitmap->height = new_bitmap->surface->h;
2352 print_timestamp_done("SDLLoadImage");
2358 // ----------------------------------------------------------------------------
2359 // custom cursor fuctions
2360 // ----------------------------------------------------------------------------
2362 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2364 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2365 cursor_info->width, cursor_info->height,
2366 cursor_info->hot_x, cursor_info->hot_y);
2369 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2371 static struct MouseCursorInfo *last_cursor_info = NULL;
2372 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2373 static SDL_Cursor *cursor_default = NULL;
2374 static SDL_Cursor *cursor_current = NULL;
2376 // if invoked for the first time, store the SDL default cursor
2377 if (cursor_default == NULL)
2378 cursor_default = SDL_GetCursor();
2380 // only create new cursor if cursor info (custom only) has changed
2381 if (cursor_info != NULL && cursor_info != last_cursor_info)
2383 cursor_current = create_cursor(cursor_info);
2384 last_cursor_info = cursor_info;
2387 // only set new cursor if cursor info (custom or NULL) has changed
2388 if (cursor_info != last_cursor_info2)
2389 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2391 last_cursor_info2 = cursor_info;
2395 // ============================================================================
2397 // ============================================================================
2399 void SDLOpenAudio(void)
2401 if (program.headless)
2404 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2406 Warn("SDL_InitSubSystem() failed: %s", SDL_GetError());
2411 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2412 AUDIO_NUM_CHANNELS_STEREO,
2413 setup.system.audio_fragment_size) < 0)
2415 Warn("Mix_OpenAudio() failed: %s", SDL_GetError());
2420 audio.sound_available = TRUE;
2421 audio.music_available = TRUE;
2422 audio.loops_available = TRUE;
2423 audio.sound_enabled = TRUE;
2425 // set number of available mixer channels
2426 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2427 audio.music_channel = MUSIC_CHANNEL;
2428 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2430 Mixer_InitChannels();
2433 void SDLCloseAudio(void)
2436 Mix_HaltChannel(-1);
2439 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2443 // ============================================================================
2445 // ============================================================================
2447 void SDLWaitEvent(Event *event)
2449 SDL_WaitEvent(event);
2452 void SDLCorrectRawMousePosition(int *x, int *y)
2454 if (sdl_renderer == NULL)
2457 // this corrects the raw mouse position for logical screen size within event
2458 // filters (correction done later by SDL library when handling mouse events)
2461 float scale_x, scale_y;
2463 SDL_RenderGetViewport(sdl_renderer, &viewport);
2464 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2466 *x = (int)(*x / scale_x);
2467 *y = (int)(*y / scale_y);
2474 // ============================================================================
2475 // joystick functions
2476 // ============================================================================
2478 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2479 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2480 static int sdl_js_axis[MAX_PLAYERS][2];
2481 static int sdl_js_button[MAX_PLAYERS][2];
2482 static boolean sdl_is_controller[MAX_PLAYERS];
2484 void SDLClearJoystickState(void)
2488 for (i = 0; i < MAX_PLAYERS; i++)
2490 for (j = 0; j < 2; j++)
2492 sdl_js_axis_raw[i][j] = -1;
2493 sdl_js_axis[i][j] = 0;
2494 sdl_js_button[i][j] = 0;
2499 boolean SDLOpenJoystick(int nr)
2501 if (nr < 0 || nr >= MAX_PLAYERS)
2504 sdl_is_controller[nr] = SDL_IsGameController(nr);
2507 Debug("joystick", "opening joystick %d (%s)",
2508 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2511 if (sdl_is_controller[nr])
2512 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2514 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2516 return (sdl_joystick[nr] != NULL);
2519 void SDLCloseJoystick(int nr)
2521 if (nr < 0 || nr >= MAX_PLAYERS)
2525 Debug("joystick", "closing joystick %d", nr);
2528 if (sdl_is_controller[nr])
2529 SDL_GameControllerClose(sdl_joystick[nr]);
2531 SDL_JoystickClose(sdl_joystick[nr]);
2533 sdl_joystick[nr] = NULL;
2536 boolean SDLCheckJoystickOpened(int nr)
2538 if (nr < 0 || nr >= MAX_PLAYERS)
2541 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2544 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2546 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2547 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2548 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2549 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2551 if (nr < 0 || nr >= MAX_PLAYERS)
2557 // prevent (slightly jittering, but centered) axis A from resetting axis B
2558 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2559 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2562 sdl_js_axis[nr][axis_id] = axis_value;
2563 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2566 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2568 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2569 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2570 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2571 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2572 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2573 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2574 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2575 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2578 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2579 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2580 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2581 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2582 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2583 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2584 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2585 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2587 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2588 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2589 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2590 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2591 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2593 if (nr < 0 || nr >= MAX_PLAYERS)
2596 if (button_id == -1)
2599 sdl_js_button[nr][button_id] = button_state;
2602 void HandleJoystickEvent(Event *event)
2604 // when using joystick, disable overlay touch buttons
2605 runtime.uses_touch_device = FALSE;
2607 switch (event->type)
2609 case SDL_CONTROLLERDEVICEADDED:
2611 Debug("joystick", "SDL_CONTROLLERDEVICEADDED: device %d added",
2612 event->cdevice.which);
2617 case SDL_CONTROLLERDEVICEREMOVED:
2619 Debug("joystick", "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2620 event->cdevice.which);
2625 case SDL_CONTROLLERAXISMOTION:
2627 Debug("joystick", "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2628 event->caxis.which, event->caxis.axis, event->caxis.value);
2630 setJoystickAxis(event->caxis.which,
2632 event->caxis.value);
2635 case SDL_CONTROLLERBUTTONDOWN:
2637 Debug("joystick", "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2638 event->cbutton.which, event->cbutton.button);
2640 setJoystickButton(event->cbutton.which,
2641 event->cbutton.button,
2645 case SDL_CONTROLLERBUTTONUP:
2647 Debug("joystick", "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2648 event->cbutton.which, event->cbutton.button);
2650 setJoystickButton(event->cbutton.which,
2651 event->cbutton.button,
2655 case SDL_JOYAXISMOTION:
2656 if (sdl_is_controller[event->jaxis.which])
2660 Debug("joystick", "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2661 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2663 if (event->jaxis.axis < 4)
2664 setJoystickAxis(event->jaxis.which,
2666 event->jaxis.value);
2669 case SDL_JOYBUTTONDOWN:
2670 if (sdl_is_controller[event->jaxis.which])
2674 Debug("joystick", "SDL_JOYBUTTONDOWN: device %d, button %d",
2675 event->jbutton.which, event->jbutton.button);
2677 if (event->jbutton.button < 4)
2678 setJoystickButton(event->jbutton.which,
2679 event->jbutton.button,
2683 case SDL_JOYBUTTONUP:
2684 if (sdl_is_controller[event->jaxis.which])
2688 Debug("joystick", "SDL_JOYBUTTONUP: device %d, button %d",
2689 event->jbutton.which, event->jbutton.button);
2691 if (event->jbutton.button < 4)
2692 setJoystickButton(event->jbutton.which,
2693 event->jbutton.button,
2702 void SDLInitJoysticks(void)
2704 static boolean sdl_joystick_subsystem_initialized = FALSE;
2705 boolean print_warning = !sdl_joystick_subsystem_initialized;
2706 char *mappings_file_base = getPath2(options.conf_directory,
2707 GAMECONTROLLER_BASENAME);
2708 char *mappings_file_user = getPath2(getMainUserGameDataDir(),
2709 GAMECONTROLLER_BASENAME);
2713 if (!sdl_joystick_subsystem_initialized)
2715 sdl_joystick_subsystem_initialized = TRUE;
2717 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2719 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2720 Fail("SDL_Init() failed: %s", SDL_GetError());
2722 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2724 // the included game controller base mappings should always be found
2725 if (num_mappings == -1)
2726 Warn("no game controller base mappings found");
2729 Debug("joystick", "%d game controller base mapping(s) added",
2733 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2736 // the personal game controller user mappings may or may not be found
2737 if (num_mappings == -1)
2738 Warn("no game controller user mappings found");
2740 Debug("joystick", , "%d game controller user mapping(s) added",
2743 Debug("joystick", "%d joystick(s) found:", SDL_NumJoysticks());
2746 checked_free(mappings_file_base);
2747 checked_free(mappings_file_user);
2750 for (i = 0; i < SDL_NumJoysticks(); i++)
2752 const char *name, *type;
2754 if (SDL_IsGameController(i))
2756 name = SDL_GameControllerNameForIndex(i);
2757 type = "game controller";
2761 name = SDL_JoystickNameForIndex(i);
2765 Debug("joystick", "- joystick %d (%s): '%s'",
2766 i, type, (name ? name : "(Unknown)"));
2771 // assign joysticks from configured to connected joystick for all players
2772 for (i = 0; i < MAX_PLAYERS; i++)
2774 // get configured joystick for this player
2775 char *device_name = setup.input[i].joy.device_name;
2776 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2778 if (joystick_nr >= SDL_NumJoysticks())
2780 if (setup.input[i].use_joystick && print_warning)
2781 Warn("cannot find joystick %d", joystick_nr);
2786 // store configured joystick number for each player
2787 joystick.nr[i] = joystick_nr;
2790 // now open all connected joysticks (regardless if configured or not)
2791 for (i = 0; i < SDL_NumJoysticks(); i++)
2793 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2794 if (SDLCheckJoystickOpened(i))
2795 SDLCloseJoystick(i);
2797 if (SDLOpenJoystick(i))
2798 joystick.status = JOYSTICK_ACTIVATED;
2799 else if (print_warning)
2800 Warn("cannot open joystick %d", i);
2803 SDLClearJoystickState();
2806 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2808 if (nr < 0 || nr >= MAX_PLAYERS)
2812 *x = sdl_js_axis[nr][0];
2814 *y = sdl_js_axis[nr][1];
2817 *b1 = sdl_js_button[nr][0];
2819 *b2 = sdl_js_button[nr][1];
2825 // ============================================================================
2826 // touch input overlay functions
2827 // ============================================================================
2829 #if defined(USE_TOUCH_INPUT_OVERLAY)
2830 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2833 int grid_xsize = overlay.grid_xsize;
2834 int grid_ysize = overlay.grid_ysize;
2837 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2838 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2840 for (x = 0; x < grid_xsize; x++)
2842 rect.x = (x + 0) * video.screen_width / grid_xsize;
2843 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2845 for (y = 0; y < grid_ysize; y++)
2847 rect.y = (y + 0) * video.screen_height / grid_ysize;
2848 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2850 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2851 SDL_RenderDrawRect(sdl_renderer, &rect);
2855 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2858 static void RenderFillRectangle(int x, int y, int width, int height)
2860 SDL_Rect rect = { x, y, width, height };
2862 SDL_RenderFillRect(sdl_renderer, &rect);
2865 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2867 static int alpha_direction = 0;
2868 static int alpha_highlight = 0;
2869 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2870 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2872 int grid_xsize = overlay.grid_xsize;
2873 int grid_ysize = overlay.grid_ysize;
2876 if (alpha == alpha_max)
2878 if (alpha_direction < 0)
2880 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2882 if (alpha_highlight == 0)
2883 alpha_direction = 1;
2887 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2889 if (alpha_highlight == alpha_max)
2890 alpha_direction = -1;
2895 alpha_direction = 1;
2896 alpha_highlight = alpha;
2899 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2901 for (x = 0; x < grid_xsize; x++)
2903 for (y = 0; y < grid_ysize; y++)
2905 int grid_button = overlay.grid_button[x][y];
2906 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2907 int alpha_draw = alpha;
2908 int outline_border = MV_NONE;
2909 int border_size = 2;
2910 boolean draw_outlined = setup.touch.draw_outlined;
2911 boolean draw_pressed = setup.touch.draw_pressed;
2913 if (grid_button == CHAR_GRID_BUTTON_NONE)
2916 if (grid_button == overlay.grid_button_highlight)
2918 draw_outlined = FALSE;
2919 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2922 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2925 draw_outlined = FALSE;
2927 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2930 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2932 rect.x = (x + 0) * video.screen_width / grid_xsize;
2933 rect.y = (y + 0) * video.screen_height / grid_ysize;
2934 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2935 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2937 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2939 rect.x += border_size;
2940 rect.w -= border_size;
2942 outline_border |= MV_LEFT;
2945 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2947 rect.w -= border_size;
2949 outline_border |= MV_RIGHT;
2952 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2954 rect.y += border_size;
2955 rect.h -= border_size;
2957 outline_border |= MV_UP;
2960 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2962 rect.h -= border_size;
2964 outline_border |= MV_DOWN;
2969 int rect_x = rect.x +
2970 (outline_border & MV_LEFT ? border_size : 0);
2971 int rect_w = rect.w -
2972 (outline_border & MV_LEFT ? border_size : 0) -
2973 (outline_border & MV_RIGHT ? border_size : 0);
2975 if (outline_border & MV_LEFT)
2976 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2978 if (outline_border & MV_RIGHT)
2979 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2980 border_size, rect.h);
2982 if (outline_border & MV_UP)
2983 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2985 if (outline_border & MV_DOWN)
2986 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
2987 rect_w, border_size);
2991 SDL_RenderFillRect(sdl_renderer, &rect);
2996 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2999 static void DrawTouchInputOverlay(void)
3001 static boolean deactivated = TRUE;
3002 static boolean show_grid = FALSE;
3003 static int alpha = 0;
3004 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3005 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3006 boolean active = (overlay.enabled && overlay.active);
3008 if (!active && deactivated)
3013 if (alpha < alpha_max)
3014 alpha = MIN(alpha + alpha_step, alpha_max);
3016 deactivated = FALSE;
3020 alpha = MAX(0, alpha - alpha_step);
3026 if (overlay.show_grid)
3028 else if (deactivated)
3032 DrawTouchInputOverlay_ShowGrid(alpha);
3034 DrawTouchInputOverlay_ShowGridButtons(alpha);
3037 static void DrawTouchGadgetsOverlay(void)
3039 DrawGadgets_OverlayTouchButtons();