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);
69 // copy envelope request to render target buffer, if needed (above all)
70 if (gfx.draw_envelope_request_function != NULL)
71 gfx.draw_envelope_request_function(draw_target);
73 // copy global animations to render target buffer, if defined (mouse pointer)
74 if (gfx.draw_global_anim_function != NULL)
75 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_3);
78 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
83 static DelayCounter update_screen_delay = { 50 }; // (milliseconds)
84 SDL_Surface *screen = backbuffer->surface;
86 if (limit_screen_updates &&
87 !DelayReached(&update_screen_delay))
90 LimitScreenUpdates(FALSE);
94 static int LastFrameCounter = 0;
95 boolean changed = (FrameCounter != LastFrameCounter);
97 Debug("internal:frame", "FrameCounter == %d [%s]", FrameCounter,
98 (changed ? "-" : "SAME FRAME UPDATED"));
100 LastFrameCounter = FrameCounter;
103 if (FrameCounter % 2)
109 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
110 gfx.final_screen_bitmap != NULL) // may not be initialized yet
112 // draw global animations using bitmaps instead of using textures
113 // to prevent texture scaling artefacts (this is potentially slower)
115 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
116 gfx.win_xsize, gfx.win_ysize, 0, 0);
118 FinalizeScreen(DRAW_TO_SCREEN);
120 screen = gfx.final_screen_bitmap->surface;
122 // force full window redraw
126 SDL_Texture *sdl_texture = sdl_texture_stream;
128 // deactivate use of target texture if render targets are not supported
129 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
130 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
131 sdl_texture_target == NULL)
132 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
134 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
135 sdl_texture = sdl_texture_target;
139 int bytes_x = screen->pitch / video.width;
140 int bytes_y = screen->pitch;
142 SDL_UpdateTexture(sdl_texture, rect,
143 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
148 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
151 int xoff = video.screen_xoffset;
152 int yoff = video.screen_yoffset;
153 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
154 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
155 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
157 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
158 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
159 dst_rect2 = &dst_rect_screen;
161 dst_rect1 = &dst_rect_screen;
163 #if defined(HAS_SCREEN_KEYBOARD)
164 SDL_Rect src_rect_up = { 0, 0, video.width, video.height };
165 SDL_Rect dst_rect_up = dst_rect_screen;
167 if (video.shifted_up || video.shifted_up_delay.count)
169 int time_current = SDL_GetTicks();
170 int pos = video.shifted_up_pos;
171 int pos_last = video.shifted_up_pos_last;
173 if (!DelayReachedExt(&video.shifted_up_delay, time_current))
175 int delay_count = time_current - video.shifted_up_delay.count;
176 int delay_value = video.shifted_up_delay.value;
178 pos = pos_last + (pos - pos_last) * delay_count / delay_value;
182 video.shifted_up_pos_last = pos;
183 video.shifted_up_delay.count = 0;
187 src_rect_up.h = video.height - pos;
188 dst_rect_up.h = video.height - pos;
190 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
191 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
193 src_rect2 = &src_rect_up;
194 dst_rect2 = &dst_rect_up;
198 src_rect1 = &src_rect_up;
199 dst_rect1 = &dst_rect_up;
204 // clear render target buffer
205 SDL_RenderClear(sdl_renderer);
207 // set renderer to use target texture for rendering
208 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
209 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
210 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
212 // copy backbuffer texture to render target buffer
213 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
214 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
216 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
217 FinalizeScreen(DRAW_TO_SCREEN);
219 // when using target texture, copy it to screen buffer
220 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
221 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
223 SDL_SetRenderTarget(sdl_renderer, NULL);
224 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
227 #if defined(USE_TOUCH_INPUT_OVERLAY)
228 // draw overlay graphics for touch device input, if needed
229 DrawTouchInputOverlay();
231 // draw overlay gadgets for touch device input, if needed
232 DrawTouchGadgetsOverlay();
235 // global synchronization point of the game to align video frame delay
236 if (with_frame_delay)
237 WaitUntilDelayReached(&video.frame_delay);
239 video.frame_counter++;
241 // show render target buffer on screen
242 SDL_RenderPresent(sdl_renderer);
245 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
247 PumpEvents(); // execute event filter actions while waiting
249 UpdateScreenExt(rect, TRUE);
252 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
254 UpdateScreenExt(rect, FALSE);
257 void Delay_WithScreenUpdates(unsigned int delay)
259 unsigned int time_current = SDL_GetTicks();
260 unsigned int time_delayed = time_current + delay;
262 while (time_current < time_delayed)
264 // updating the screen contains waiting for frame delay (non-busy)
265 UpdateScreen_WithFrameDelay(NULL);
267 time_current = SDL_GetTicks();
271 static void SDLSetWindowIcon(char *basename)
273 // (setting the window icon on Mac OS X would replace the high-quality
274 // dock icon with the currently smaller (and uglier) icon from file)
276 #if !defined(PLATFORM_MAC)
277 char *filename = getCustomImageFilename(basename);
278 SDL_Surface *surface;
280 if (filename == NULL)
282 Warn("SDLSetWindowIcon(): cannot find file '%s'", basename);
287 if ((surface = IMG_Load(filename)) == NULL)
289 Warn("IMG_Load('%s') failed: %s", basename, SDL_GetError());
294 // set transparent color
295 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
296 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
298 SDL_SetWindowIcon(sdl_window, surface);
302 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
303 SDL_PixelFormat *format2)
305 return (format1->format == format2->format &&
306 format1->BitsPerPixel == format2->BitsPerPixel &&
307 format1->BytesPerPixel == format2->BytesPerPixel &&
308 format1->Rmask == format2->Rmask &&
309 format1->Gmask == format2->Gmask &&
310 format1->Bmask == format2->Bmask);
313 static void SDLCopyColorKey(SDL_Surface *src_surface, SDL_Surface *dst_surface)
318 // check if source surface has a color key
319 if (SDL_GetColorKey(src_surface, &color_key) == 0)
321 // get RGB values of color key of source surface
322 SDL_GetRGB(color_key, src_surface->format, &r, &g, &b);
324 // get color key from RGB values in destination surface format
325 color_key = SDL_MapRGB(dst_surface->format, r, g, b);
327 // set color key in destination surface
328 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL, color_key);
332 // unset color key in destination surface
333 SDL_SetColorKey(dst_surface, UNSET_TRANSPARENT_PIXEL, 0);
337 static boolean SDLHasColorKey(SDL_Surface *surface)
341 return (SDL_GetColorKey(surface, &color_key) == 0);
344 static boolean SDLHasAlpha(SDL_Surface *surface)
346 SDL_BlendMode blend_mode;
348 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
351 return (blend_mode == SDL_BLENDMODE_BLEND);
354 void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
356 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
358 SDL_SetSurfaceBlendMode(surface, blend_mode);
359 SDL_SetSurfaceAlphaMod(surface, alpha);
362 const char *SDLGetRendererName(void)
364 static SDL_RendererInfo renderer_info;
366 SDL_GetRendererInfo(sdl_renderer, &renderer_info);
368 return renderer_info.name;
371 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
373 SDL_PixelFormat format;
374 SDL_Surface *new_surface;
379 if (backbuffer && backbuffer->surface)
381 format = *backbuffer->surface->format;
382 format.Amask = surface->format->Amask; // keep alpha channel
386 format = *surface->format;
389 new_surface = SDL_ConvertSurface(surface, &format, 0);
391 if (new_surface == NULL)
392 Fail("SDL_ConvertSurface() failed: %s", SDL_GetError());
394 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
395 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
396 SDLCopyColorKey(surface, new_surface);
401 boolean SDLSetNativeSurface(SDL_Surface **surface)
403 SDL_Surface *new_surface;
405 if (surface == NULL ||
407 backbuffer == NULL ||
408 backbuffer->surface == NULL)
411 // if pixel format already optimized for destination surface, do nothing
412 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
415 new_surface = SDLGetNativeSurface(*surface);
417 SDL_FreeSurface(*surface);
419 *surface = new_surface;
424 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
426 if (program.headless)
429 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
432 Fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
437 void SDLCreateBitmapTextures(Bitmap *bitmap)
443 SDL_DestroyTexture(bitmap->texture);
444 if (bitmap->texture_masked)
445 SDL_DestroyTexture(bitmap->texture_masked);
447 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
448 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
451 void SDLFreeBitmapTextures(Bitmap *bitmap)
457 SDL_DestroyTexture(bitmap->texture);
458 if (bitmap->texture_masked)
459 SDL_DestroyTexture(bitmap->texture_masked);
461 bitmap->texture = NULL;
462 bitmap->texture_masked = NULL;
465 void SDLInitVideoDisplay(void)
467 // set hint to select render driver as specified in setup config file
468 if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
469 SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
471 // initialize SDL video
472 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
473 Fail("SDL_InitSubSystem() failed: %s", SDL_GetError());
475 // set default SDL depth
476 video.default_depth = 32; // (how to determine video depth in SDL2?)
478 // Code used with SDL 1.2:
479 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
482 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
484 if (program.headless)
487 video.window_scaling_percent = setup.window_scaling_percent;
488 video.window_scaling_quality = setup.window_scaling_quality;
490 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
492 // SDL 2.0: support for (desktop) fullscreen mode available
493 video.fullscreen_available = TRUE;
495 // open SDL video output device (window or fullscreen mode)
496 if (!SDLSetVideoMode(fullscreen))
497 Fail("setting video mode failed");
499 // !!! SDL2 can only set the window icon if the window already exists !!!
501 SDLSetWindowIcon(program.icon_filename);
503 // set window and icon title
507 static void SDLInitVideoBuffer_DrawBuffer(void)
509 /* SDL cannot directly draw to the visible video framebuffer like X11,
510 but always uses a backbuffer, which is then blitted to the visible
511 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
512 visible video framebuffer with 'SDL_Flip', if the hardware supports
513 this). Therefore do not use an additional backbuffer for drawing, but
514 use a symbolic buffer (distinguishable from the SDL backbuffer) called
515 'window', which indicates that the SDL backbuffer should be updated to
516 the visible video framebuffer when attempting to blit to it.
518 For convenience, it seems to be a good idea to create this symbolic
519 buffer 'window' at the same size as the SDL backbuffer. Although it
520 should never be drawn to directly, it would do no harm nevertheless. */
522 // create additional (symbolic) buffer for double-buffering
523 ReCreateBitmap(&window, video.width, video.height);
525 // create dummy drawing buffer for headless mode, if needed
526 if (program.headless)
527 ReCreateBitmap(&backbuffer, video.width, video.height);
530 void SDLInitVideoBuffer(boolean fullscreen)
532 SDLInitVideoBuffer_VideoBuffer(fullscreen);
533 SDLInitVideoBuffer_DrawBuffer();
536 static boolean SDLCreateScreen(boolean fullscreen)
538 SDL_Surface *new_surface = NULL;
540 int surface_flags_window = SURFACE_FLAGS;
541 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
544 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
546 video.vsync_mode = VSYNC_MODE_OFF;
548 if (!strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF))
550 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
551 video.vsync_mode = VSYNC_MODE_NORMAL;
554 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
555 _without_ enabling 2D/3D acceleration and/or guest additions installed,
556 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
557 it will try to use accelerated graphics and apparently fails miserably) */
558 int renderer_flags = SDL_RENDERER_SOFTWARE;
561 int width = video.width;
562 int height = video.height;
563 int screen_width = video.screen_width;
564 int screen_height = video.screen_height;
565 int surface_flags = (fullscreen ? surface_flags_fullscreen :
566 surface_flags_window);
567 int display_nr = options.display_nr;
569 // default window size is unscaled
570 video.window_width = screen_width;
571 video.window_height = screen_height;
573 // store if initial screen mode is fullscreen mode when changing screen size
574 video.fullscreen_initial = fullscreen;
576 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
578 video.window_width = window_scaling_factor * screen_width;
579 video.window_height = window_scaling_factor * screen_height;
581 if (sdl_texture_stream)
583 SDL_DestroyTexture(sdl_texture_stream);
584 sdl_texture_stream = NULL;
587 if (sdl_texture_target)
589 SDL_DestroyTexture(sdl_texture_target);
590 sdl_texture_target = NULL;
593 if (!(fullscreen && fullscreen_enabled))
597 SDL_DestroyRenderer(sdl_renderer);
603 SDL_SetWindowSize(sdl_window, video.window_width, video.window_height);
607 if (sdl_window == NULL)
608 sdl_window = SDL_CreateWindow(program.window_title,
609 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
610 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
615 if (sdl_window != NULL)
617 if (sdl_renderer == NULL)
618 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
620 if (sdl_renderer != NULL)
622 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
623 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
625 // required for setting adaptive vsync when using OpenGL renderer
626 SDLSetScreenVsyncMode(setup.vsync_mode);
628 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
629 SDL_PIXELFORMAT_ARGB8888,
630 SDL_TEXTUREACCESS_STREAMING,
633 if (SDL_RenderTargetSupported(sdl_renderer))
634 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
635 SDL_PIXELFORMAT_ARGB8888,
636 SDL_TEXTUREACCESS_TARGET,
639 if (sdl_texture_stream != NULL)
641 // use SDL default values for RGB masks and no alpha channel
642 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
644 if (new_surface == NULL)
645 Warn("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
649 Warn("SDL_CreateTexture() failed: %s", SDL_GetError());
654 Warn("SDL_CreateRenderer() failed: %s", SDL_GetError());
659 Warn("SDL_CreateWindow() failed: %s", SDL_GetError());
662 SDLSetScreenProperties();
664 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
665 if (new_surface != NULL)
666 fullscreen_enabled = fullscreen;
668 if (backbuffer == NULL)
669 backbuffer = CreateBitmapStruct();
671 backbuffer->width = video.width;
672 backbuffer->height = video.height;
674 if (backbuffer->surface)
675 SDL_FreeSurface(backbuffer->surface);
677 backbuffer->surface = new_surface;
679 return (new_surface != NULL);
682 boolean SDLSetVideoMode(boolean fullscreen)
684 boolean success = FALSE;
688 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
690 // switch display to fullscreen mode, if available
691 success = SDLCreateScreen(TRUE);
695 // switching display to fullscreen mode failed -- do not try it again
696 video.fullscreen_available = FALSE;
700 video.fullscreen_enabled = TRUE;
704 if ((!fullscreen && video.fullscreen_enabled) || !success)
706 // switch display to window mode
707 success = SDLCreateScreen(FALSE);
711 // switching display to window mode failed -- should not happen
715 video.fullscreen_enabled = FALSE;
716 video.window_scaling_percent = setup.window_scaling_percent;
717 video.window_scaling_quality = setup.window_scaling_quality;
719 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
723 SDLRedrawWindow(); // map window
728 void SDLSetWindowTitle(void)
730 if (sdl_window == NULL)
733 SDL_SetWindowTitle(sdl_window, program.window_title);
736 void SDLSetWindowScaling(int window_scaling_percent)
738 if (sdl_window == NULL)
741 float window_scaling_factor = (float)window_scaling_percent / 100;
742 int new_window_width = (int)(window_scaling_factor * video.screen_width);
743 int new_window_height = (int)(window_scaling_factor * video.screen_height);
745 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
747 video.window_scaling_percent = window_scaling_percent;
748 video.window_width = new_window_width;
749 video.window_height = new_window_height;
754 void SDLSetWindowScalingQuality(char *window_scaling_quality)
756 SDL_Texture *new_texture;
758 if (sdl_texture_stream == NULL)
761 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
763 new_texture = SDL_CreateTexture(sdl_renderer,
764 SDL_PIXELFORMAT_ARGB8888,
765 SDL_TEXTUREACCESS_STREAMING,
766 video.width, video.height);
768 if (new_texture != NULL)
770 SDL_DestroyTexture(sdl_texture_stream);
772 sdl_texture_stream = new_texture;
775 if (SDL_RenderTargetSupported(sdl_renderer))
776 new_texture = SDL_CreateTexture(sdl_renderer,
777 SDL_PIXELFORMAT_ARGB8888,
778 SDL_TEXTUREACCESS_TARGET,
779 video.width, video.height);
783 if (new_texture != NULL)
785 SDL_DestroyTexture(sdl_texture_target);
787 sdl_texture_target = new_texture;
792 video.window_scaling_quality = window_scaling_quality;
795 void SDLSetWindowFullscreen(boolean fullscreen)
797 if (sdl_window == NULL)
800 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
802 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
803 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
805 // if screen size was changed in fullscreen mode, correct desktop window size
806 if (!fullscreen && video.fullscreen_initial)
808 SDLSetWindowScaling(setup.window_scaling_percent);
809 SDL_SetWindowPosition(sdl_window,
810 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr),
811 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr));
813 video.fullscreen_initial = FALSE;
817 void SDLSetDisplaySize(void)
819 if (sdl_renderer != NULL)
823 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
825 video.display_width = w;
826 video.display_height = h;
829 Debug("video", "SDL renderer size: %d x %d",
830 video.display_width, video.display_height);
835 SDL_Rect display_bounds;
837 SDL_GetDisplayBounds(0, &display_bounds);
839 video.display_width = display_bounds.w;
840 video.display_height = display_bounds.h;
843 Debug("video", "SDL display size: %d x %d",
844 video.display_width, video.display_height);
849 void SDLSetScreenSizeAndOffsets(int width, int height)
851 // set default video screen size and offsets
852 video.screen_width = width;
853 video.screen_height = height;
854 video.screen_xoffset = 0;
855 video.screen_yoffset = 0;
857 #if defined(USE_COMPLETE_DISPLAY)
858 float ratio_video = (float) width / height;
859 float ratio_display = (float) video.display_width / video.display_height;
861 if (ratio_video != ratio_display)
863 // adjust drawable screen size to cover the whole device display
865 if (ratio_video < ratio_display)
866 video.screen_width *= ratio_display / ratio_video;
868 video.screen_height *= ratio_video / ratio_display;
870 video.screen_xoffset = (video.screen_width - width) / 2;
871 video.screen_yoffset = (video.screen_height - height) / 2;
874 Debug("video", "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
876 video.screen_width, video.screen_height,
877 ratio_video, ratio_display);
883 void SDLSetScreenSizeForRenderer(int width, int height)
885 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
888 void SDLSetScreenProperties(void)
891 SDLSetScreenSizeAndOffsets(video.width, video.height);
892 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
894 SetOverlayGridSizeAndButtons();
897 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
899 video.screen_rendering_mode =
900 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
901 SPECIAL_RENDERING_BITMAP :
902 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
903 SPECIAL_RENDERING_TARGET:
904 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
905 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
908 void SDLSetScreenVsyncMode(char *vsync_mode)
910 // changing vsync mode without re-creating renderer only supported by OpenGL
911 if (!strPrefixLower((char *)SDLGetRendererName(), "opengl"))
914 int interval = VSYNC_MODE_STR_TO_INT(vsync_mode);
915 int result = SDL_GL_SetSwapInterval(interval);
917 // if adaptive vsync requested, but not supported, retry with normal vsync
918 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
920 interval = VSYNC_MODE_NORMAL;
922 result = SDL_GL_SetSwapInterval(interval);
926 interval = VSYNC_MODE_OFF;
928 video.vsync_mode = interval;
931 void SDLRedrawWindow(void)
933 UpdateScreen_WithoutFrameDelay(NULL);
936 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
939 if (program.headless)
942 SDL_Surface *surface =
943 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
946 Fail("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
948 SDLSetNativeSurface(&surface);
950 bitmap->surface = surface;
953 void SDLFreeBitmapPointers(Bitmap *bitmap)
956 SDL_FreeSurface(bitmap->surface);
957 if (bitmap->surface_masked)
958 SDL_FreeSurface(bitmap->surface_masked);
960 bitmap->surface = NULL;
961 bitmap->surface_masked = NULL;
964 SDL_DestroyTexture(bitmap->texture);
965 if (bitmap->texture_masked)
966 SDL_DestroyTexture(bitmap->texture_masked);
968 bitmap->texture = NULL;
969 bitmap->texture_masked = NULL;
972 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
973 int src_x, int src_y, int width, int height,
974 int dst_x, int dst_y, int mask_mode)
976 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
977 SDL_Rect src_rect, dst_rect;
989 // if (src_bitmap != backbuffer || dst_bitmap != window)
990 if (!(src_bitmap == backbuffer && dst_bitmap == window))
991 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
992 src_bitmap->surface_masked : src_bitmap->surface),
993 &src_rect, real_dst_bitmap->surface, &dst_rect);
995 if (dst_bitmap == window)
996 UpdateScreen_WithFrameDelay(&dst_rect);
999 void SDLBlitTexture(Bitmap *bitmap,
1000 int src_x, int src_y, int width, int height,
1001 int dst_x, int dst_y, int mask_mode)
1003 SDL_Texture *texture;
1008 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1010 if (texture == NULL)
1016 src_rect.h = height;
1021 dst_rect.h = height;
1023 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1026 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1029 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1037 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1039 if (dst_bitmap == window)
1040 UpdateScreen_WithFrameDelay(&rect);
1043 void PrepareFadeBitmap(int draw_target)
1045 Bitmap *fade_bitmap =
1046 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1047 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1049 if (fade_bitmap == NULL)
1052 // copy backbuffer to fading buffer
1053 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1055 // add border and animations to fading buffer
1056 FinalizeScreen(draw_target);
1059 void SDLFadeRectangle(int x, int y, int width, int height,
1060 int fade_mode, int fade_delay, int post_delay,
1061 void (*draw_border_function)(void))
1063 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1064 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1065 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1066 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1067 SDL_Surface *surface_screen = backbuffer->surface;
1068 SDL_Rect src_rect, dst_rect;
1070 int src_x = x, src_y = y;
1071 int dst_x = x, dst_y = y;
1072 unsigned int time_last, time_current;
1074 // store function for drawing global masked border
1075 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1077 // deactivate drawing of global border while fading, if needed
1078 if (draw_border_function == NULL)
1079 gfx.draw_global_border_function = NULL;
1084 src_rect.h = height;
1088 dst_rect.w = width; // (ignored)
1089 dst_rect.h = height; // (ignored)
1091 dst_rect2 = dst_rect;
1093 // before fading in, store backbuffer (without animation graphics)
1094 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1095 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1097 // copy source and target surfaces to temporary surfaces for fading
1098 if (fade_mode & FADE_TYPE_TRANSFORM)
1100 // (source and target fading buffer already prepared)
1102 else if (fade_mode & FADE_TYPE_FADE_IN)
1104 // (target fading buffer already prepared)
1105 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1107 else // FADE_TYPE_FADE_OUT
1109 // (source fading buffer already prepared)
1110 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1113 time_current = SDL_GetTicks();
1115 if (fade_delay <= 0)
1117 // immediately draw final target frame without delay
1118 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1122 // when fading without delay, also skip post delay
1126 if (fade_mode == FADE_MODE_MELT)
1128 boolean done = FALSE;
1129 int melt_pixels = 2;
1130 int melt_columns = width / melt_pixels;
1131 int ypos[melt_columns];
1132 int max_steps = height / 8 + 32;
1137 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1139 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1141 ypos[0] = -GetSimpleRandom(16);
1143 for (i = 1 ; i < melt_columns; i++)
1145 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1147 ypos[i] = ypos[i - 1] + r;
1160 time_last = time_current;
1161 time_current = SDL_GetTicks();
1162 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1163 steps_final = MIN(MAX(0, steps), max_steps);
1167 done = (steps_done >= steps_final);
1169 for (i = 0 ; i < melt_columns; i++)
1177 else if (ypos[i] < height)
1182 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1184 if (ypos[i] + dy >= height)
1185 dy = height - ypos[i];
1187 // copy part of (appearing) target surface to upper area
1188 src_rect.x = src_x + i * melt_pixels;
1189 // src_rect.y = src_y + ypos[i];
1191 src_rect.w = melt_pixels;
1193 src_rect.h = ypos[i] + dy;
1195 dst_rect.x = dst_x + i * melt_pixels;
1196 // dst_rect.y = dst_y + ypos[i];
1199 if (steps_done >= steps_final)
1200 SDL_BlitSurface(surface_target, &src_rect,
1201 surface_screen, &dst_rect);
1205 // copy part of (disappearing) source surface to lower area
1206 src_rect.x = src_x + i * melt_pixels;
1208 src_rect.w = melt_pixels;
1209 src_rect.h = height - ypos[i];
1211 dst_rect.x = dst_x + i * melt_pixels;
1212 dst_rect.y = dst_y + ypos[i];
1214 if (steps_done >= steps_final)
1215 SDL_BlitSurface(surface_source, &src_rect,
1216 surface_screen, &dst_rect);
1222 src_rect.x = src_x + i * melt_pixels;
1224 src_rect.w = melt_pixels;
1225 src_rect.h = height;
1227 dst_rect.x = dst_x + i * melt_pixels;
1230 if (steps_done >= steps_final)
1231 SDL_BlitSurface(surface_target, &src_rect,
1232 surface_screen, &dst_rect);
1236 if (steps_done >= steps_final)
1238 if (draw_border_function != NULL)
1239 draw_border_function();
1241 UpdateScreen_WithFrameDelay(&dst_rect2);
1243 if (PendingEscapeKeyEvent())
1248 else if (fade_mode == FADE_MODE_CURTAIN)
1252 int xx_size = width / 2;
1254 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1256 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1258 for (xx = 0; xx < xx_size;)
1260 time_last = time_current;
1261 time_current = SDL_GetTicks();
1262 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1263 xx_final = MIN(MAX(0, xx), xx_size);
1268 src_rect.h = height;
1273 // draw new (target) image to screen buffer
1274 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1276 if (xx_final < xx_size)
1278 src_rect.w = xx_size - xx_final;
1279 src_rect.h = height;
1281 // draw old (source) image to screen buffer (left side)
1283 src_rect.x = src_x + xx_final;
1286 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1288 // draw old (source) image to screen buffer (right side)
1290 src_rect.x = src_x + xx_size;
1291 dst_rect.x = dst_x + xx_size + xx_final;
1293 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1296 if (draw_border_function != NULL)
1297 draw_border_function();
1299 // only update the region of the screen that is affected from fading
1300 UpdateScreen_WithFrameDelay(&dst_rect2);
1302 if (PendingEscapeKeyEvent())
1306 else // fading in, fading out or cross-fading
1311 for (alpha = 0.0; alpha < 255.0;)
1313 time_last = time_current;
1314 time_current = SDL_GetTicks();
1315 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1316 alpha_final = MIN(MAX(0, alpha), 255);
1318 // draw existing (source) image to screen buffer
1319 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1321 // draw new (target) image to screen buffer using alpha blending
1322 SDLSetAlpha(surface_target, TRUE, alpha_final);
1323 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1325 if (draw_border_function != NULL)
1326 draw_border_function();
1328 // only update the region of the screen that is affected from fading
1329 UpdateScreen_WithFrameDelay(&dst_rect);
1331 if (PendingEscapeKeyEvent())
1337 Delay_WithScreenUpdates(post_delay);
1339 // restore function for drawing global masked border
1340 gfx.draw_global_border_function = draw_global_border_function;
1342 // after fading in, restore backbuffer (without animation graphics)
1343 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1344 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1347 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1348 int to_x, int to_y, Uint32 color)
1350 SDL_Surface *surface = dst_bitmap->surface;
1354 swap_numbers(&from_x, &to_x);
1357 swap_numbers(&from_y, &to_y);
1361 rect.w = (to_x - from_x + 1);
1362 rect.h = (to_y - from_y + 1);
1364 SDL_FillRect(surface, &rect, color);
1367 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1368 int to_x, int to_y, Uint32 color)
1370 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1373 #if ENABLE_UNUSED_CODE
1374 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1375 int num_points, Uint32 color)
1380 for (i = 0; i < num_points - 1; i++)
1382 for (x = 0; x < line_width; x++)
1384 for (y = 0; y < line_width; y++)
1386 int dx = x - line_width / 2;
1387 int dy = y - line_width / 2;
1389 if ((x == 0 && y == 0) ||
1390 (x == 0 && y == line_width - 1) ||
1391 (x == line_width - 1 && y == 0) ||
1392 (x == line_width - 1 && y == line_width - 1))
1395 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1396 points[i+1].x + dx, points[i+1].y + dy, color);
1403 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1405 SDL_Surface *surface = src_bitmap->surface;
1407 switch (surface->format->BytesPerPixel)
1409 case 1: // assuming 8-bpp
1411 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1415 case 2: // probably 15-bpp or 16-bpp
1417 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1421 case 3: // slow 24-bpp mode; usually not used
1424 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1428 shift = surface->format->Rshift;
1429 color |= *(pix + shift / 8) >> shift;
1430 shift = surface->format->Gshift;
1431 color |= *(pix + shift / 8) >> shift;
1432 shift = surface->format->Bshift;
1433 color |= *(pix + shift / 8) >> shift;
1439 case 4: // probably 32-bpp
1441 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1450 // ============================================================================
1451 // The following functions were taken from the SGE library
1452 // (SDL Graphics Extension Library) by Anders Lindström
1453 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1454 // ============================================================================
1456 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1458 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1460 switch (surface->format->BytesPerPixel)
1465 *((Uint8 *)surface->pixels + y * surface->pitch + x) = color;
1471 // Probably 15-bpp or 16-bpp
1472 *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x) = color;
1478 // Slow 24-bpp mode, usually not used
1482 // Gack - slow, but endian correct
1483 pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1484 shift = surface->format->Rshift;
1485 *(pix + shift / 8) = color>>shift;
1486 shift = surface->format->Gshift;
1487 *(pix + shift / 8) = color>>shift;
1488 shift = surface->format->Bshift;
1489 *(pix + shift / 8) = color>>shift;
1496 *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x) = color;
1504 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1505 Uint8 R, Uint8 G, Uint8 B)
1507 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1510 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1512 *((Uint8 *)surface->pixels + y * surface->pitch + x) = color;
1515 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1517 *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x) = color;
1520 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1525 // Gack - slow, but endian correct
1526 pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1527 shift = surface->format->Rshift;
1528 *(pix + shift / 8) = color>>shift;
1529 shift = surface->format->Gshift;
1530 *(pix + shift / 8) = color>>shift;
1531 shift = surface->format->Bshift;
1532 *(pix + shift / 8) = color>>shift;
1535 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1537 *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x) = color;
1540 static void _PutPixelX(SDL_Surface *dest, Sint16 x, Sint16 y, Uint32 color)
1542 switch (dest->format->BytesPerPixel)
1545 *((Uint8 *)dest->pixels + y * dest->pitch + x) = color;
1549 *((Uint16 *)dest->pixels + y * dest->pitch / 2 + x) = color;
1553 _PutPixel24(dest, x, y, color);
1557 *((Uint32 *)dest->pixels + y * dest->pitch / 4 + x) = color;
1563 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1565 if (SDL_MUSTLOCK(surface))
1567 if (SDL_LockSurface(surface) < 0)
1573 _PutPixel(surface, x, y, color);
1575 if (SDL_MUSTLOCK(surface))
1577 SDL_UnlockSurface(surface);
1582 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1583 Uint8 r, Uint8 g, Uint8 b)
1585 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1588 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1590 if (y >= 0 && y <= dest->h - 1)
1592 switch (dest->format->BytesPerPixel)
1595 return y * dest->pitch;
1599 return y * dest->pitch / 2;
1603 return y * dest->pitch;
1607 return y * dest->pitch / 4;
1615 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1618 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1620 switch (surface->format->BytesPerPixel)
1625 *((Uint8 *)surface->pixels + ypitch + x) = color;
1631 // Probably 15-bpp or 16-bpp
1632 *((Uint16 *)surface->pixels + ypitch + x) = color;
1638 // Slow 24-bpp mode, usually not used
1642 // Gack - slow, but endian correct
1643 pix = (Uint8 *)surface->pixels + ypitch + x * 3;
1644 shift = surface->format->Rshift;
1645 *(pix + shift / 8) = color>>shift;
1646 shift = surface->format->Gshift;
1647 *(pix + shift / 8) = color>>shift;
1648 shift = surface->format->Bshift;
1649 *(pix + shift / 8) = color>>shift;
1656 *((Uint32 *)surface->pixels + ypitch + x) = color;
1663 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1668 if (SDL_MUSTLOCK(Surface))
1670 if (SDL_LockSurface(Surface) < 0)
1684 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1688 if (x2 > Surface->w - 1)
1689 x2 = Surface->w - 1;
1696 SDL_FillRect(Surface, &l, Color);
1698 if (SDL_MUSTLOCK(Surface))
1700 SDL_UnlockSurface(Surface);
1704 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1705 Uint8 R, Uint8 G, Uint8 B)
1707 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1710 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1723 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1727 if (x2 > Surface->w - 1)
1728 x2 = Surface->w - 1;
1735 SDL_FillRect(Surface, &l, Color);
1738 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1743 if (SDL_MUSTLOCK(Surface))
1745 if (SDL_LockSurface(Surface) < 0)
1759 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1763 if (y2 > Surface->h - 1)
1764 y2 = Surface->h - 1;
1771 SDL_FillRect(Surface, &l, Color);
1773 if (SDL_MUSTLOCK(Surface))
1775 SDL_UnlockSurface(Surface);
1779 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1780 Uint8 R, Uint8 G, Uint8 B)
1782 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1785 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1798 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1802 if (y2 > Surface->h - 1)
1803 y2 = Surface->h - 1;
1810 SDL_FillRect(Surface, &l, Color);
1814 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1815 Sint16 x2, Sint16 y2, Uint32 Color,
1816 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1819 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1824 sdx = (dx < 0) ? -1 : 1;
1825 sdy = (dy < 0) ? -1 : 1;
1837 for (x = 0; x < dx; x++)
1839 Callback(Surface, px, py, Color);
1853 for (y = 0; y < dy; y++)
1855 Callback(Surface, px, py, Color);
1870 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1871 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1872 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1875 sge_DoLine(Surface, X1, Y1, X2, Y2,
1876 SDL_MapRGB(Surface->format, R, G, B), Callback);
1880 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1883 if (SDL_MUSTLOCK(Surface))
1885 if (SDL_LockSurface(Surface) < 0)
1890 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1892 // unlock the display
1893 if (SDL_MUSTLOCK(Surface))
1895 SDL_UnlockSurface(Surface);
1900 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1901 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1903 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1907 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1909 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1913 // ----------------------------------------------------------------------------
1914 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1915 // ----------------------------------------------------------------------------
1917 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1918 int src_x, int src_y, int width, int height,
1919 int dst_x, int dst_y)
1923 for (y = 0; y < height; y++)
1925 for (x = 0; x < width; x++)
1927 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1929 if (pixel != BLACK_PIXEL)
1930 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1936 // ============================================================================
1937 // The following functions were taken from the SDL_gfx library version 2.0.3
1938 // (Rotozoomer) by Andreas Schiffler
1939 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1940 // ============================================================================
1942 // ----------------------------------------------------------------------------
1945 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1946 // ----------------------------------------------------------------------------
1956 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1959 tColorRGBA *sp, *csp, *dp;
1963 sp = csp = (tColorRGBA *) src->pixels;
1964 dp = (tColorRGBA *) dst->pixels;
1965 dgap = dst->pitch - dst->w * 4;
1967 for (y = 0; y < dst->h; y++)
1971 for (x = 0; x < dst->w; x++)
1973 tColorRGBA *sp0 = sp;
1974 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1975 tColorRGBA *sp00 = &sp0[0];
1976 tColorRGBA *sp01 = &sp0[1];
1977 tColorRGBA *sp10 = &sp1[0];
1978 tColorRGBA *sp11 = &sp1[1];
1981 // create new color pixel from all four source color pixels
1982 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1983 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1984 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1985 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1990 // advance source pointers
1993 // advance destination pointer
1997 // advance source pointer
1998 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2000 // advance destination pointers
2001 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2007 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2009 int x, y, *sax, *say, *csax, *csay;
2011 tColorRGBA *sp, *csp, *csp0, *dp;
2014 // use specialized zoom function when scaling down to exactly half size
2015 if (src->w == 2 * dst->w &&
2016 src->h == 2 * dst->h)
2017 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2020 sx = (float) src->w / (float) dst->w;
2021 sy = (float) src->h / (float) dst->h;
2023 // allocate memory for row increments
2024 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2025 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2027 // precalculate row increments
2028 for (x = 0; x <= dst->w; x++)
2029 *csax++ = (int)(sx * x);
2031 for (y = 0; y <= dst->h; y++)
2032 *csay++ = (int)(sy * y);
2035 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2036 dp = (tColorRGBA *) dst->pixels;
2037 dgap = dst->pitch - dst->w * 4;
2040 for (y = 0; y < dst->h; y++)
2045 for (x = 0; x < dst->w; x++)
2050 // advance source pointers
2054 // advance destination pointer
2058 // advance source pointer
2060 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2062 // advance destination pointers
2063 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2072 // ----------------------------------------------------------------------------
2075 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2076 // ----------------------------------------------------------------------------
2078 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2080 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2081 Uint8 *sp, *dp, *csp;
2085 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2086 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2088 // allocate memory for row increments
2089 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2090 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2092 // precalculate row increments
2095 for (x = 0; x < dst->w; x++)
2098 *csax = (csx >> 16);
2105 for (y = 0; y < dst->h; y++)
2108 *csay = (csy >> 16);
2115 for (x = 0; x < dst->w; x++)
2123 for (y = 0; y < dst->h; y++)
2130 sp = csp = (Uint8 *) src->pixels;
2131 dp = (Uint8 *) dst->pixels;
2132 dgap = dst->pitch - dst->w;
2136 for (y = 0; y < dst->h; y++)
2140 for (x = 0; x < dst->w; x++)
2145 // advance source pointers
2149 // advance destination pointer
2153 // advance source pointer (for row)
2154 csp += ((*csay) * src->pitch);
2157 // advance destination pointers
2167 // ----------------------------------------------------------------------------
2170 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2171 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2172 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2173 // into a 32bit RGBA format on the fly.
2174 // ----------------------------------------------------------------------------
2176 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2178 SDL_Surface *zoom_src = NULL;
2179 SDL_Surface *zoom_dst = NULL;
2180 boolean is_converted = FALSE;
2187 // determine if source surface is 32 bit or 8 bit
2188 is_32bit = (src->format->BitsPerPixel == 32);
2190 if (is_32bit || src->format->BitsPerPixel == 8)
2192 // use source surface 'as is'
2197 // new source surface is 32 bit with a defined RGB ordering
2198 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2199 0x000000ff, 0x0000ff00, 0x00ff0000,
2200 (src->format->Amask ? 0xff000000 : 0));
2201 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2203 is_converted = TRUE;
2206 // allocate surface to completely contain the zoomed surface
2209 // target surface is 32 bit with source RGBA/ABGR ordering
2210 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2211 zoom_src->format->Rmask,
2212 zoom_src->format->Gmask,
2213 zoom_src->format->Bmask,
2214 zoom_src->format->Amask);
2218 // target surface is 8 bit
2219 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2223 // lock source surface
2224 SDL_LockSurface(zoom_src);
2226 // check which kind of surface we have
2229 // call the 32 bit transformation routine to do the zooming
2230 zoomSurfaceRGBA(zoom_src, zoom_dst);
2235 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2236 zoom_dst->format->palette->colors[i] =
2237 zoom_src->format->palette->colors[i];
2238 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2240 // call the 8 bit transformation routine to do the zooming
2241 zoomSurfaceY(zoom_src, zoom_dst);
2244 // unlock source surface
2245 SDL_UnlockSurface(zoom_src);
2247 // free temporary surface
2249 SDL_FreeSurface(zoom_src);
2251 // return destination surface
2255 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2257 SDL_Surface *new_surface;
2259 if (surface == NULL)
2262 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2263 Fail("SDLGetNativeSurface() failed");
2265 // remove alpha channel from native non-transparent surface, if defined
2266 SDLSetAlpha(new_surface, FALSE, 0);
2268 // remove transparent color from native non-transparent surface, if defined
2269 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2274 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2276 Bitmap *dst_bitmap = CreateBitmapStruct();
2277 SDL_Surface *src_surface = src_bitmap->surface_masked;
2278 SDL_Surface *dst_surface;
2280 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2281 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2283 dst_bitmap->width = dst_width;
2284 dst_bitmap->height = dst_height;
2286 // create zoomed temporary surface from source surface
2287 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2289 // create native format destination surface from zoomed temporary surface
2290 SDLSetNativeSurface(&dst_surface);
2292 // set color key for zoomed surface from source surface, if defined
2293 if (SDLHasColorKey(src_surface))
2294 SDLCopyColorKey(src_surface, dst_surface);
2296 // create native non-transparent surface for opaque blitting
2297 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2299 // set native transparent surface for masked blitting
2300 dst_bitmap->surface_masked = dst_surface;
2306 // ============================================================================
2307 // load image to bitmap
2308 // ============================================================================
2310 Bitmap *SDLLoadImage(char *filename)
2312 Bitmap *new_bitmap = CreateBitmapStruct();
2313 SDL_Surface *sdl_image_tmp;
2315 if (program.headless)
2317 // prevent sanity check warnings at later stage
2318 new_bitmap->width = new_bitmap->height = 1;
2323 print_timestamp_init("SDLLoadImage");
2325 print_timestamp_time(getBaseNamePtr(filename));
2327 // load image to temporary surface
2328 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2329 Fail("IMG_Load('%s') failed: %s", getBaseNamePtr(filename), SDL_GetError());
2331 print_timestamp_time("IMG_Load");
2333 UPDATE_BUSY_STATE();
2335 // create native non-transparent surface for current image
2336 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2337 Fail("SDLGetOpaqueSurface() failed");
2339 print_timestamp_time("SDLGetNativeSurface (opaque)");
2341 UPDATE_BUSY_STATE();
2343 // set black pixel to transparent if no alpha channel / transparent color
2344 if (!SDLHasAlpha(sdl_image_tmp) &&
2345 !SDLHasColorKey(sdl_image_tmp))
2346 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2347 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2349 // create native transparent surface for current image
2350 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2351 Fail("SDLGetNativeSurface() failed");
2353 print_timestamp_time("SDLGetNativeSurface (masked)");
2355 UPDATE_BUSY_STATE();
2357 // free temporary surface
2358 SDL_FreeSurface(sdl_image_tmp);
2360 new_bitmap->width = new_bitmap->surface->w;
2361 new_bitmap->height = new_bitmap->surface->h;
2363 print_timestamp_done("SDLLoadImage");
2369 // ----------------------------------------------------------------------------
2370 // custom cursor fuctions
2371 // ----------------------------------------------------------------------------
2373 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2375 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2376 cursor_info->width, cursor_info->height,
2377 cursor_info->hot_x, cursor_info->hot_y);
2380 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2382 static struct MouseCursorInfo *last_cursor_info = NULL;
2383 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2384 static SDL_Cursor *cursor_default = NULL;
2385 static SDL_Cursor *cursor_current = NULL;
2387 // if invoked for the first time, store the SDL default cursor
2388 if (cursor_default == NULL)
2389 cursor_default = SDL_GetCursor();
2391 // only create new cursor if cursor info (custom only) has changed
2392 if (cursor_info != NULL && cursor_info != last_cursor_info)
2394 cursor_current = create_cursor(cursor_info);
2395 last_cursor_info = cursor_info;
2398 // only set new cursor if cursor info (custom or NULL) has changed
2399 if (cursor_info != last_cursor_info2)
2400 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2402 last_cursor_info2 = cursor_info;
2406 // ============================================================================
2408 // ============================================================================
2410 void SDLOpenAudio(void)
2412 if (program.headless)
2415 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2417 Warn("SDL_InitSubSystem() failed: %s", SDL_GetError());
2422 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2423 AUDIO_NUM_CHANNELS_STEREO,
2424 setup.system.audio_fragment_size) < 0)
2426 Warn("Mix_OpenAudio() failed: %s", SDL_GetError());
2431 audio.sound_available = TRUE;
2432 audio.music_available = TRUE;
2433 audio.loops_available = TRUE;
2434 audio.sound_enabled = TRUE;
2436 // set number of available mixer channels
2437 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2438 audio.music_channel = MUSIC_CHANNEL;
2439 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2441 Mixer_InitChannels();
2444 void SDLCloseAudio(void)
2447 Mix_HaltChannel(-1);
2450 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2454 // ============================================================================
2456 // ============================================================================
2458 void SDLWaitEvent(Event *event)
2460 SDL_WaitEvent(event);
2463 void SDLCorrectRawMousePosition(int *x, int *y)
2465 if (sdl_renderer == NULL)
2468 // this corrects the raw mouse position for logical screen size within event
2469 // filters (correction done later by SDL library when handling mouse events)
2472 float scale_x, scale_y;
2474 SDL_RenderGetViewport(sdl_renderer, &viewport);
2475 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2477 *x = (int)(*x / scale_x);
2478 *y = (int)(*y / scale_y);
2485 // ============================================================================
2486 // joystick functions
2487 // ============================================================================
2489 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2490 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2491 static int sdl_js_axis[MAX_PLAYERS][2];
2492 static int sdl_js_button[MAX_PLAYERS][2];
2493 static boolean sdl_is_controller[MAX_PLAYERS];
2495 void SDLClearJoystickState(void)
2499 for (i = 0; i < MAX_PLAYERS; i++)
2501 for (j = 0; j < 2; j++)
2503 sdl_js_axis_raw[i][j] = -1;
2504 sdl_js_axis[i][j] = 0;
2505 sdl_js_button[i][j] = 0;
2510 boolean SDLOpenJoystick(int nr)
2512 if (nr < 0 || nr >= MAX_PLAYERS)
2515 sdl_is_controller[nr] = SDL_IsGameController(nr);
2518 Debug("joystick", "opening joystick %d (%s)",
2519 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2522 if (sdl_is_controller[nr])
2523 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2525 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2527 return (sdl_joystick[nr] != NULL);
2530 void SDLCloseJoystick(int nr)
2532 if (nr < 0 || nr >= MAX_PLAYERS)
2536 Debug("joystick", "closing joystick %d", nr);
2539 if (sdl_is_controller[nr])
2540 SDL_GameControllerClose(sdl_joystick[nr]);
2542 SDL_JoystickClose(sdl_joystick[nr]);
2544 sdl_joystick[nr] = NULL;
2547 boolean SDLCheckJoystickOpened(int nr)
2549 if (nr < 0 || nr >= MAX_PLAYERS)
2552 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2555 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2557 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2558 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2559 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2560 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2562 if (nr < 0 || nr >= MAX_PLAYERS)
2568 // prevent (slightly jittering, but centered) axis A from resetting axis B
2569 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2570 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2573 sdl_js_axis[nr][axis_id] = axis_value;
2574 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2577 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2579 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2580 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2581 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2582 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2583 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2584 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2585 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2586 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2589 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2590 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2591 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2592 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2593 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2594 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2595 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2596 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2598 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2599 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2600 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2601 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2602 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2604 if (nr < 0 || nr >= MAX_PLAYERS)
2607 if (button_id == -1)
2610 sdl_js_button[nr][button_id] = button_state;
2613 void HandleJoystickEvent(Event *event)
2615 // when using joystick, disable overlay touch buttons
2616 runtime.uses_touch_device = FALSE;
2618 switch (event->type)
2620 case SDL_CONTROLLERDEVICEADDED:
2622 Debug("joystick", "SDL_CONTROLLERDEVICEADDED: device %d added",
2623 event->cdevice.which);
2628 case SDL_CONTROLLERDEVICEREMOVED:
2630 Debug("joystick", "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2631 event->cdevice.which);
2636 case SDL_CONTROLLERAXISMOTION:
2638 Debug("joystick", "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2639 event->caxis.which, event->caxis.axis, event->caxis.value);
2641 setJoystickAxis(event->caxis.which,
2643 event->caxis.value);
2646 case SDL_CONTROLLERBUTTONDOWN:
2648 Debug("joystick", "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2649 event->cbutton.which, event->cbutton.button);
2651 setJoystickButton(event->cbutton.which,
2652 event->cbutton.button,
2656 case SDL_CONTROLLERBUTTONUP:
2658 Debug("joystick", "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2659 event->cbutton.which, event->cbutton.button);
2661 setJoystickButton(event->cbutton.which,
2662 event->cbutton.button,
2666 case SDL_JOYAXISMOTION:
2667 if (sdl_is_controller[event->jaxis.which])
2671 Debug("joystick", "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2672 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2674 if (event->jaxis.axis < 4)
2675 setJoystickAxis(event->jaxis.which,
2677 event->jaxis.value);
2680 case SDL_JOYBUTTONDOWN:
2681 if (sdl_is_controller[event->jaxis.which])
2685 Debug("joystick", "SDL_JOYBUTTONDOWN: device %d, button %d",
2686 event->jbutton.which, event->jbutton.button);
2688 if (event->jbutton.button < 4)
2689 setJoystickButton(event->jbutton.which,
2690 event->jbutton.button,
2694 case SDL_JOYBUTTONUP:
2695 if (sdl_is_controller[event->jaxis.which])
2699 Debug("joystick", "SDL_JOYBUTTONUP: device %d, button %d",
2700 event->jbutton.which, event->jbutton.button);
2702 if (event->jbutton.button < 4)
2703 setJoystickButton(event->jbutton.which,
2704 event->jbutton.button,
2713 void SDLInitJoysticks(void)
2715 static boolean sdl_joystick_subsystem_initialized = FALSE;
2716 boolean print_warning = !sdl_joystick_subsystem_initialized;
2717 char *mappings_file_base = getPath2(options.conf_directory,
2718 GAMECONTROLLER_BASENAME);
2719 char *mappings_file_user = getPath2(getMainUserGameDataDir(),
2720 GAMECONTROLLER_BASENAME);
2724 if (!sdl_joystick_subsystem_initialized)
2726 sdl_joystick_subsystem_initialized = TRUE;
2728 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2730 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2731 Fail("SDL_Init() failed: %s", SDL_GetError());
2733 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2735 // the included game controller base mappings should always be found
2736 if (num_mappings == -1)
2737 Warn("no game controller base mappings found");
2740 Debug("joystick", "%d game controller base mapping(s) added",
2744 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2747 // the personal game controller user mappings may or may not be found
2748 if (num_mappings == -1)
2749 Warn("no game controller user mappings found");
2751 Debug("joystick", , "%d game controller user mapping(s) added",
2754 Debug("joystick", "%d joystick(s) found:", SDL_NumJoysticks());
2757 checked_free(mappings_file_base);
2758 checked_free(mappings_file_user);
2761 for (i = 0; i < SDL_NumJoysticks(); i++)
2763 const char *name, *type;
2765 if (SDL_IsGameController(i))
2767 name = SDL_GameControllerNameForIndex(i);
2768 type = "game controller";
2772 name = SDL_JoystickNameForIndex(i);
2776 Debug("joystick", "- joystick %d (%s): '%s'",
2777 i, type, (name ? name : "(Unknown)"));
2782 // assign joysticks from configured to connected joystick for all players
2783 for (i = 0; i < MAX_PLAYERS; i++)
2785 // get configured joystick for this player
2786 char *device_name = setup.input[i].joy.device_name;
2787 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2789 if (joystick_nr >= SDL_NumJoysticks())
2791 if (setup.input[i].use_joystick && print_warning)
2792 Warn("cannot find joystick %d", joystick_nr);
2797 // store configured joystick number for each player
2798 joystick.nr[i] = joystick_nr;
2801 // now open all connected joysticks (regardless if configured or not)
2802 for (i = 0; i < SDL_NumJoysticks(); i++)
2804 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2805 if (SDLCheckJoystickOpened(i))
2806 SDLCloseJoystick(i);
2808 if (SDLOpenJoystick(i))
2809 joystick.status = JOYSTICK_ACTIVATED;
2810 else if (print_warning)
2811 Warn("cannot open joystick %d", i);
2814 SDLClearJoystickState();
2817 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2819 if (nr < 0 || nr >= MAX_PLAYERS)
2823 *x = sdl_js_axis[nr][0];
2825 *y = sdl_js_axis[nr][1];
2828 *b1 = sdl_js_button[nr][0];
2830 *b2 = sdl_js_button[nr][1];
2836 // ============================================================================
2837 // touch input overlay functions
2838 // ============================================================================
2840 #if defined(USE_TOUCH_INPUT_OVERLAY)
2841 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2844 int grid_xsize = overlay.grid_xsize;
2845 int grid_ysize = overlay.grid_ysize;
2848 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2849 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2851 for (x = 0; x < grid_xsize; x++)
2853 rect.x = (x + 0) * video.screen_width / grid_xsize;
2854 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2856 for (y = 0; y < grid_ysize; y++)
2858 rect.y = (y + 0) * video.screen_height / grid_ysize;
2859 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2861 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2862 SDL_RenderDrawRect(sdl_renderer, &rect);
2866 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2869 static void RenderFillRectangle(int x, int y, int width, int height)
2871 SDL_Rect rect = { x, y, width, height };
2873 SDL_RenderFillRect(sdl_renderer, &rect);
2876 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2878 static int alpha_direction = 0;
2879 static int alpha_highlight = 0;
2880 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2881 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2883 int grid_xsize = overlay.grid_xsize;
2884 int grid_ysize = overlay.grid_ysize;
2887 if (alpha == alpha_max)
2889 if (alpha_direction < 0)
2891 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2893 if (alpha_highlight == 0)
2894 alpha_direction = 1;
2898 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2900 if (alpha_highlight == alpha_max)
2901 alpha_direction = -1;
2906 alpha_direction = 1;
2907 alpha_highlight = alpha;
2910 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2912 for (x = 0; x < grid_xsize; x++)
2914 for (y = 0; y < grid_ysize; y++)
2916 int grid_button = overlay.grid_button[x][y];
2917 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2918 int alpha_draw = alpha;
2919 int outline_border = MV_NONE;
2920 int border_size = 2;
2921 boolean draw_outlined = setup.touch.draw_outlined;
2922 boolean draw_pressed = setup.touch.draw_pressed;
2924 if (grid_button == CHAR_GRID_BUTTON_NONE)
2927 if (grid_button == overlay.grid_button_highlight)
2929 draw_outlined = FALSE;
2930 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2933 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2936 draw_outlined = FALSE;
2938 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2941 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2943 rect.x = (x + 0) * video.screen_width / grid_xsize;
2944 rect.y = (y + 0) * video.screen_height / grid_ysize;
2945 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2946 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2948 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2950 rect.x += border_size;
2951 rect.w -= border_size;
2953 outline_border |= MV_LEFT;
2956 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2958 rect.w -= border_size;
2960 outline_border |= MV_RIGHT;
2963 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2965 rect.y += border_size;
2966 rect.h -= border_size;
2968 outline_border |= MV_UP;
2971 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2973 rect.h -= border_size;
2975 outline_border |= MV_DOWN;
2980 int rect_x = rect.x +
2981 (outline_border & MV_LEFT ? border_size : 0);
2982 int rect_w = rect.w -
2983 (outline_border & MV_LEFT ? border_size : 0) -
2984 (outline_border & MV_RIGHT ? border_size : 0);
2986 if (outline_border & MV_LEFT)
2987 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2989 if (outline_border & MV_RIGHT)
2990 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2991 border_size, rect.h);
2993 if (outline_border & MV_UP)
2994 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2996 if (outline_border & MV_DOWN)
2997 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
2998 rect_w, border_size);
3002 SDL_RenderFillRect(sdl_renderer, &rect);
3007 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3010 static void DrawTouchInputOverlay(void)
3012 static boolean deactivated = TRUE;
3013 static boolean show_grid = FALSE;
3014 static int alpha = 0;
3015 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3016 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3017 boolean active = (overlay.enabled && overlay.active);
3019 if (!active && deactivated)
3024 if (alpha < alpha_max)
3025 alpha = MIN(alpha + alpha_step, alpha_max);
3027 deactivated = FALSE;
3031 alpha = MAX(0, alpha - alpha_step);
3037 if (overlay.show_grid)
3039 else if (deactivated)
3043 DrawTouchInputOverlay_ShowGrid(alpha);
3045 DrawTouchInputOverlay_ShowGridButtons(alpha);
3048 static void DrawTouchGadgetsOverlay(void)
3050 DrawGadgets_OverlayTouchButtons();