1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
19 #define ENABLE_UNUSED_CODE 0 // currently unused functions
21 #define DEBUG_JOYSTICKS 0
24 // ============================================================================
26 // ============================================================================
28 // SDL internal variables
29 static SDL_Window *sdl_window = NULL;
30 static SDL_Renderer *sdl_renderer = NULL;
31 static SDL_Texture *sdl_texture_stream = NULL;
32 static SDL_Texture *sdl_texture_target = NULL;
33 static boolean fullscreen_enabled = FALSE;
34 static boolean limit_screen_updates = FALSE;
37 // functions from SGE library
38 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
40 #if defined(USE_TOUCH_INPUT_OVERLAY)
41 // functions to draw overlay graphics for touch device input
42 static void DrawTouchInputOverlay(void);
43 static void DrawTouchGadgetsOverlay(void);
46 void SDLLimitScreenUpdates(boolean enable)
48 limit_screen_updates = enable;
51 static void FinalizeScreen(int draw_target)
53 // copy global animations to render target buffer, if defined (below border)
54 if (gfx.draw_global_anim_function != NULL)
55 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
57 // copy global masked border to render target buffer, if defined
58 if (gfx.draw_global_border_function != NULL)
59 gfx.draw_global_border_function(draw_target);
61 // copy global animations to render target buffer, if defined (above border)
62 if (gfx.draw_global_anim_function != NULL)
63 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
65 // copy tile selection cursor to render target buffer, if defined (above all)
66 if (gfx.draw_tile_cursor_function != NULL)
67 gfx.draw_tile_cursor_function(draw_target);
70 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
72 static unsigned int update_screen_delay = 0;
73 unsigned int update_screen_delay_value = 50; // (milliseconds)
74 SDL_Surface *screen = backbuffer->surface;
76 if (limit_screen_updates &&
77 !DelayReached(&update_screen_delay, update_screen_delay_value))
80 LimitScreenUpdates(FALSE);
84 static int LastFrameCounter = 0;
85 boolean changed = (FrameCounter != LastFrameCounter);
87 Debug("internal:frame", "FrameCounter == %d [%s]", FrameCounter,
88 (changed ? "-" : "SAME FRAME UPDATED"));
90 LastFrameCounter = FrameCounter;
99 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
100 gfx.final_screen_bitmap != NULL) // may not be initialized yet
102 // draw global animations using bitmaps instead of using textures
103 // to prevent texture scaling artefacts (this is potentially slower)
105 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
106 gfx.win_xsize, gfx.win_ysize, 0, 0);
108 FinalizeScreen(DRAW_TO_SCREEN);
110 screen = gfx.final_screen_bitmap->surface;
112 // force full window redraw
116 SDL_Texture *sdl_texture = sdl_texture_stream;
118 // deactivate use of target texture if render targets are not supported
119 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
120 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
121 sdl_texture_target == NULL)
122 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
124 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
125 sdl_texture = sdl_texture_target;
129 int bytes_x = screen->pitch / video.width;
130 int bytes_y = screen->pitch;
132 SDL_UpdateTexture(sdl_texture, rect,
133 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
138 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
141 int xoff = video.screen_xoffset;
142 int yoff = video.screen_yoffset;
143 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
144 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
145 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
147 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
148 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
149 dst_rect2 = &dst_rect_screen;
151 dst_rect1 = &dst_rect_screen;
153 #if defined(HAS_SCREEN_KEYBOARD)
154 if (video.shifted_up || video.shifted_up_delay)
156 int time_current = SDL_GetTicks();
157 int pos = video.shifted_up_pos;
158 int pos_last = video.shifted_up_pos_last;
160 if (!DelayReachedExt(&video.shifted_up_delay, video.shifted_up_delay_value,
163 int delay = time_current - video.shifted_up_delay;
164 int delay_value = video.shifted_up_delay_value;
166 pos = pos_last + (pos - pos_last) * delay / delay_value;
170 video.shifted_up_pos_last = pos;
171 video.shifted_up_delay = 0;
174 SDL_Rect src_rect_up = { 0, pos, video.width, video.height - pos };
175 SDL_Rect dst_rect_up = { xoff, yoff, video.width, video.height - pos };
177 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
178 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
180 src_rect2 = &src_rect_up;
181 dst_rect2 = &dst_rect_up;
185 src_rect1 = &src_rect_up;
186 dst_rect1 = &dst_rect_up;
191 // clear render target buffer
192 SDL_RenderClear(sdl_renderer);
194 // set renderer to use target texture for rendering
195 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
196 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
197 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
199 // copy backbuffer texture to render target buffer
200 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
201 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
203 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
204 FinalizeScreen(DRAW_TO_SCREEN);
206 // when using target texture, copy it to screen buffer
207 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
208 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
210 SDL_SetRenderTarget(sdl_renderer, NULL);
211 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
214 #if defined(USE_TOUCH_INPUT_OVERLAY)
215 // draw overlay graphics for touch device input, if needed
216 DrawTouchInputOverlay();
218 // draw overlay gadgets for touch device input, if needed
219 DrawTouchGadgetsOverlay();
222 // global synchronization point of the game to align video frame delay
223 if (with_frame_delay)
224 WaitUntilDelayReached(&video.frame_delay, video.frame_delay_value);
226 video.frame_counter++;
228 // show render target buffer on screen
229 SDL_RenderPresent(sdl_renderer);
232 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
234 PumpEvents(); // execute event filter actions while waiting
236 UpdateScreenExt(rect, TRUE);
239 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
241 UpdateScreenExt(rect, FALSE);
244 void Delay_WithScreenUpdates(unsigned int delay)
246 unsigned int time_current = SDL_GetTicks();
247 unsigned int time_delayed = time_current + delay;
249 while (time_current < time_delayed)
251 // updating the screen contains waiting for frame delay (non-busy)
252 UpdateScreen_WithFrameDelay(NULL);
254 time_current = SDL_GetTicks();
258 static void SDLSetWindowIcon(char *basename)
260 // (setting the window icon on Mac OS X would replace the high-quality
261 // dock icon with the currently smaller (and uglier) icon from file)
263 #if !defined(PLATFORM_MACOSX)
264 char *filename = getCustomImageFilename(basename);
265 SDL_Surface *surface;
267 if (filename == NULL)
269 Warn("SDLSetWindowIcon(): cannot find file '%s'", basename);
274 if ((surface = IMG_Load(filename)) == NULL)
276 Warn("IMG_Load('%s') failed: %s", basename, SDL_GetError());
281 // set transparent color
282 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
283 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
285 SDL_SetWindowIcon(sdl_window, surface);
289 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
290 SDL_PixelFormat *format2)
292 return (format1->format == format2->format &&
293 format1->BitsPerPixel == format2->BitsPerPixel &&
294 format1->BytesPerPixel == format2->BytesPerPixel &&
295 format1->Rmask == format2->Rmask &&
296 format1->Gmask == format2->Gmask &&
297 format1->Bmask == format2->Bmask);
301 static Pixel SDLGetColorKey(SDL_Surface *surface)
305 if (SDL_GetColorKey(surface, &color_key) != 0)
312 static void SDLCopyColorKey(SDL_Surface *src_surface, SDL_Surface *dst_surface)
317 // check if source surface has a color key
318 if (SDL_GetColorKey(src_surface, &color_key) == 0)
320 // get RGB values of color key of source surface
321 SDL_GetRGB(color_key, src_surface->format, &r, &g, &b);
323 // get color key from RGB values in destination surface format
324 color_key = SDL_MapRGB(dst_surface->format, r, g, b);
326 // set color key in destination surface
327 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL, color_key);
331 // unset color key in destination surface
332 SDL_SetColorKey(dst_surface, UNSET_TRANSPARENT_PIXEL, 0);
336 static boolean SDLHasColorKey(SDL_Surface *surface)
340 return (SDL_GetColorKey(surface, &color_key) == 0);
343 static boolean SDLHasAlpha(SDL_Surface *surface)
345 SDL_BlendMode blend_mode;
347 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
350 return (blend_mode == SDL_BLENDMODE_BLEND);
353 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
355 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
357 SDL_SetSurfaceBlendMode(surface, blend_mode);
358 SDL_SetSurfaceAlphaMod(surface, alpha);
361 const char *SDLGetRendererName(void)
363 static SDL_RendererInfo renderer_info;
365 SDL_GetRendererInfo(sdl_renderer, &renderer_info);
367 return renderer_info.name;
370 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
372 SDL_PixelFormat format;
373 SDL_Surface *new_surface;
378 if (backbuffer && backbuffer->surface)
380 format = *backbuffer->surface->format;
381 format.Amask = surface->format->Amask; // keep alpha channel
385 format = *surface->format;
388 new_surface = SDL_ConvertSurface(surface, &format, 0);
390 if (new_surface == NULL)
391 Fail("SDL_ConvertSurface() failed: %s", SDL_GetError());
393 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
394 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
395 SDLCopyColorKey(surface, new_surface);
400 boolean SDLSetNativeSurface(SDL_Surface **surface)
402 SDL_Surface *new_surface;
404 if (surface == NULL ||
406 backbuffer == NULL ||
407 backbuffer->surface == NULL)
410 // if pixel format already optimized for destination surface, do nothing
411 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
414 new_surface = SDLGetNativeSurface(*surface);
416 SDL_FreeSurface(*surface);
418 *surface = new_surface;
423 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
425 if (program.headless)
428 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
431 Fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
436 void SDLCreateBitmapTextures(Bitmap *bitmap)
442 SDL_DestroyTexture(bitmap->texture);
443 if (bitmap->texture_masked)
444 SDL_DestroyTexture(bitmap->texture_masked);
446 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
447 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
450 void SDLFreeBitmapTextures(Bitmap *bitmap)
456 SDL_DestroyTexture(bitmap->texture);
457 if (bitmap->texture_masked)
458 SDL_DestroyTexture(bitmap->texture_masked);
460 bitmap->texture = NULL;
461 bitmap->texture_masked = NULL;
464 void SDLInitVideoDisplay(void)
466 // set hint to select render driver as specified in setup config file
467 if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
468 SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
470 // initialize SDL video
471 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
472 Fail("SDL_InitSubSystem() failed: %s", SDL_GetError());
474 // set default SDL depth
475 video.default_depth = 32; // (how to determine video depth in SDL2?)
477 // Code used with SDL 1.2:
478 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
481 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
483 if (program.headless)
486 video.window_scaling_percent = setup.window_scaling_percent;
487 video.window_scaling_quality = setup.window_scaling_quality;
489 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
491 // SDL 2.0: support for (desktop) fullscreen mode available
492 video.fullscreen_available = TRUE;
494 // open SDL video output device (window or fullscreen mode)
495 if (!SDLSetVideoMode(fullscreen))
496 Fail("setting video mode failed");
498 // !!! SDL2 can only set the window icon if the window already exists !!!
500 SDLSetWindowIcon(program.icon_filename);
502 // set window and icon title
506 static void SDLInitVideoBuffer_DrawBuffer(void)
508 /* SDL cannot directly draw to the visible video framebuffer like X11,
509 but always uses a backbuffer, which is then blitted to the visible
510 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
511 visible video framebuffer with 'SDL_Flip', if the hardware supports
512 this). Therefore do not use an additional backbuffer for drawing, but
513 use a symbolic buffer (distinguishable from the SDL backbuffer) called
514 'window', which indicates that the SDL backbuffer should be updated to
515 the visible video framebuffer when attempting to blit to it.
517 For convenience, it seems to be a good idea to create this symbolic
518 buffer 'window' at the same size as the SDL backbuffer. Although it
519 should never be drawn to directly, it would do no harm nevertheless. */
521 // create additional (symbolic) buffer for double-buffering
522 ReCreateBitmap(&window, video.width, video.height);
524 // create dummy drawing buffer for headless mode, if needed
525 if (program.headless)
526 ReCreateBitmap(&backbuffer, video.width, video.height);
529 void SDLInitVideoBuffer(boolean fullscreen)
531 SDLInitVideoBuffer_VideoBuffer(fullscreen);
532 SDLInitVideoBuffer_DrawBuffer();
535 static boolean SDLCreateScreen(boolean fullscreen)
537 SDL_Surface *new_surface = NULL;
539 int surface_flags_window = SURFACE_FLAGS;
540 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
543 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
545 video.vsync_mode = VSYNC_MODE_OFF;
547 if (!strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF))
549 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
550 video.vsync_mode = VSYNC_MODE_NORMAL;
553 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
554 _without_ enabling 2D/3D acceleration and/or guest additions installed,
555 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
556 it will try to use accelerated graphics and apparently fails miserably) */
557 int renderer_flags = SDL_RENDERER_SOFTWARE;
560 int width = video.width;
561 int height = video.height;
562 int screen_width = video.screen_width;
563 int screen_height = video.screen_height;
564 int surface_flags = (fullscreen ? surface_flags_fullscreen :
565 surface_flags_window);
567 // default window size is unscaled
568 video.window_width = screen_width;
569 video.window_height = screen_height;
571 // store if initial screen mode is fullscreen mode when changing screen size
572 video.fullscreen_initial = fullscreen;
574 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
576 video.window_width = window_scaling_factor * screen_width;
577 video.window_height = window_scaling_factor * screen_height;
579 if (sdl_texture_stream)
581 SDL_DestroyTexture(sdl_texture_stream);
582 sdl_texture_stream = NULL;
585 if (sdl_texture_target)
587 SDL_DestroyTexture(sdl_texture_target);
588 sdl_texture_target = NULL;
591 if (!(fullscreen && fullscreen_enabled))
595 SDL_DestroyRenderer(sdl_renderer);
601 SDL_DestroyWindow(sdl_window);
606 if (sdl_window == NULL)
607 sdl_window = SDL_CreateWindow(program.window_title,
608 SDL_WINDOWPOS_CENTERED,
609 SDL_WINDOWPOS_CENTERED,
614 if (sdl_window != NULL)
616 if (sdl_renderer == NULL)
617 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
619 if (sdl_renderer != NULL)
621 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
622 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
624 // required for setting adaptive vsync when using OpenGL renderer
625 SDLSetScreenVsyncMode(setup.vsync_mode);
627 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
628 SDL_PIXELFORMAT_ARGB8888,
629 SDL_TEXTUREACCESS_STREAMING,
632 if (SDL_RenderTargetSupported(sdl_renderer))
633 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
634 SDL_PIXELFORMAT_ARGB8888,
635 SDL_TEXTUREACCESS_TARGET,
638 if (sdl_texture_stream != NULL)
640 // use SDL default values for RGB masks and no alpha channel
641 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
643 if (new_surface == NULL)
644 Warn("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
648 Warn("SDL_CreateTexture() failed: %s", SDL_GetError());
653 Warn("SDL_CreateRenderer() failed: %s", SDL_GetError());
658 Warn("SDL_CreateWindow() failed: %s", SDL_GetError());
661 SDLSetScreenProperties();
663 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
664 if (new_surface != NULL)
665 fullscreen_enabled = fullscreen;
667 if (backbuffer == NULL)
668 backbuffer = CreateBitmapStruct();
670 backbuffer->width = video.width;
671 backbuffer->height = video.height;
673 if (backbuffer->surface)
674 SDL_FreeSurface(backbuffer->surface);
676 backbuffer->surface = new_surface;
678 return (new_surface != NULL);
681 boolean SDLSetVideoMode(boolean fullscreen)
683 boolean success = FALSE;
687 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
689 // switch display to fullscreen mode, if available
690 success = SDLCreateScreen(TRUE);
694 // switching display to fullscreen mode failed -- do not try it again
695 video.fullscreen_available = FALSE;
699 video.fullscreen_enabled = TRUE;
703 if ((!fullscreen && video.fullscreen_enabled) || !success)
705 // switch display to window mode
706 success = SDLCreateScreen(FALSE);
710 // switching display to window mode failed -- should not happen
714 video.fullscreen_enabled = FALSE;
715 video.window_scaling_percent = setup.window_scaling_percent;
716 video.window_scaling_quality = setup.window_scaling_quality;
718 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
722 SDLRedrawWindow(); // map window
727 void SDLSetWindowTitle(void)
729 if (sdl_window == NULL)
732 SDL_SetWindowTitle(sdl_window, program.window_title);
735 void SDLSetWindowScaling(int window_scaling_percent)
737 if (sdl_window == NULL)
740 float window_scaling_factor = (float)window_scaling_percent / 100;
741 int new_window_width = (int)(window_scaling_factor * video.screen_width);
742 int new_window_height = (int)(window_scaling_factor * video.screen_height);
744 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
746 video.window_scaling_percent = window_scaling_percent;
747 video.window_width = new_window_width;
748 video.window_height = new_window_height;
753 void SDLSetWindowScalingQuality(char *window_scaling_quality)
755 SDL_Texture *new_texture;
757 if (sdl_texture_stream == NULL)
760 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
762 new_texture = SDL_CreateTexture(sdl_renderer,
763 SDL_PIXELFORMAT_ARGB8888,
764 SDL_TEXTUREACCESS_STREAMING,
765 video.width, video.height);
767 if (new_texture != NULL)
769 SDL_DestroyTexture(sdl_texture_stream);
771 sdl_texture_stream = new_texture;
774 if (SDL_RenderTargetSupported(sdl_renderer))
775 new_texture = SDL_CreateTexture(sdl_renderer,
776 SDL_PIXELFORMAT_ARGB8888,
777 SDL_TEXTUREACCESS_TARGET,
778 video.width, video.height);
782 if (new_texture != NULL)
784 SDL_DestroyTexture(sdl_texture_target);
786 sdl_texture_target = new_texture;
791 video.window_scaling_quality = window_scaling_quality;
794 void SDLSetWindowFullscreen(boolean fullscreen)
796 if (sdl_window == NULL)
799 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
801 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
802 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
804 // if screen size was changed in fullscreen mode, correct desktop window size
805 if (!fullscreen && video.fullscreen_initial)
807 SDLSetWindowScaling(setup.window_scaling_percent);
808 SDL_SetWindowPosition(sdl_window,
809 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
811 video.fullscreen_initial = FALSE;
815 void SDLSetDisplaySize(void)
817 if (sdl_renderer != NULL)
821 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
823 video.display_width = w;
824 video.display_height = h;
827 Debug("video", "SDL renderer size: %d x %d",
828 video.display_width, video.display_height);
833 SDL_Rect display_bounds;
835 SDL_GetDisplayBounds(0, &display_bounds);
837 video.display_width = display_bounds.w;
838 video.display_height = display_bounds.h;
841 Debug("video", "SDL display size: %d x %d",
842 video.display_width, video.display_height);
847 void SDLSetScreenSizeAndOffsets(int width, int height)
849 // set default video screen size and offsets
850 video.screen_width = width;
851 video.screen_height = height;
852 video.screen_xoffset = 0;
853 video.screen_yoffset = 0;
855 #if defined(USE_COMPLETE_DISPLAY)
856 float ratio_video = (float) width / height;
857 float ratio_display = (float) video.display_width / video.display_height;
859 if (ratio_video != ratio_display)
861 // adjust drawable screen size to cover the whole device display
863 if (ratio_video < ratio_display)
864 video.screen_width *= ratio_display / ratio_video;
866 video.screen_height *= ratio_video / ratio_display;
868 video.screen_xoffset = (video.screen_width - width) / 2;
869 video.screen_yoffset = (video.screen_height - height) / 2;
872 Debug("video", "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
874 video.screen_width, video.screen_height,
875 ratio_video, ratio_display);
881 void SDLSetScreenSizeForRenderer(int width, int height)
883 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
886 void SDLSetScreenProperties(void)
889 SDLSetScreenSizeAndOffsets(video.width, video.height);
890 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
893 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
895 video.screen_rendering_mode =
896 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
897 SPECIAL_RENDERING_BITMAP :
898 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
899 SPECIAL_RENDERING_TARGET:
900 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
901 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
904 void SDLSetScreenVsyncMode(char *vsync_mode)
906 // changing vsync mode without re-creating renderer only supported by OpenGL
907 if (!strPrefixLower((char *)SDLGetRendererName(), "opengl"))
910 int interval = VSYNC_MODE_STR_TO_INT(vsync_mode);
911 int result = SDL_GL_SetSwapInterval(interval);
913 // if adaptive vsync requested, but not supported, retry with normal vsync
914 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
916 interval = VSYNC_MODE_NORMAL;
918 result = SDL_GL_SetSwapInterval(interval);
922 interval = VSYNC_MODE_OFF;
924 video.vsync_mode = interval;
927 void SDLRedrawWindow(void)
929 UpdateScreen_WithoutFrameDelay(NULL);
932 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
935 if (program.headless)
938 SDL_Surface *surface =
939 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
942 Fail("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
944 SDLSetNativeSurface(&surface);
946 bitmap->surface = surface;
949 void SDLFreeBitmapPointers(Bitmap *bitmap)
952 SDL_FreeSurface(bitmap->surface);
953 if (bitmap->surface_masked)
954 SDL_FreeSurface(bitmap->surface_masked);
956 bitmap->surface = NULL;
957 bitmap->surface_masked = NULL;
960 SDL_DestroyTexture(bitmap->texture);
961 if (bitmap->texture_masked)
962 SDL_DestroyTexture(bitmap->texture_masked);
964 bitmap->texture = NULL;
965 bitmap->texture_masked = NULL;
968 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
969 int src_x, int src_y, int width, int height,
970 int dst_x, int dst_y, int mask_mode)
972 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
973 SDL_Rect src_rect, dst_rect;
985 // if (src_bitmap != backbuffer || dst_bitmap != window)
986 if (!(src_bitmap == backbuffer && dst_bitmap == window))
987 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
988 src_bitmap->surface_masked : src_bitmap->surface),
989 &src_rect, real_dst_bitmap->surface, &dst_rect);
991 if (dst_bitmap == window)
992 UpdateScreen_WithFrameDelay(&dst_rect);
995 void SDLBlitTexture(Bitmap *bitmap,
996 int src_x, int src_y, int width, int height,
997 int dst_x, int dst_y, int mask_mode)
999 SDL_Texture *texture;
1004 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1006 if (texture == NULL)
1012 src_rect.h = height;
1017 dst_rect.h = height;
1019 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1022 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1025 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1033 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1035 if (dst_bitmap == window)
1036 UpdateScreen_WithFrameDelay(&rect);
1039 void PrepareFadeBitmap(int draw_target)
1041 Bitmap *fade_bitmap =
1042 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1043 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1045 if (fade_bitmap == NULL)
1048 // copy backbuffer to fading buffer
1049 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1051 // add border and animations to fading buffer
1052 FinalizeScreen(draw_target);
1055 void SDLFadeRectangle(int x, int y, int width, int height,
1056 int fade_mode, int fade_delay, int post_delay,
1057 void (*draw_border_function)(void))
1059 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1060 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1061 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1062 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1063 SDL_Surface *surface_screen = backbuffer->surface;
1064 SDL_Rect src_rect, dst_rect;
1066 int src_x = x, src_y = y;
1067 int dst_x = x, dst_y = y;
1068 unsigned int time_last, time_current;
1070 // store function for drawing global masked border
1071 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1073 // deactivate drawing of global border while fading, if needed
1074 if (draw_border_function == NULL)
1075 gfx.draw_global_border_function = NULL;
1080 src_rect.h = height;
1084 dst_rect.w = width; // (ignored)
1085 dst_rect.h = height; // (ignored)
1087 dst_rect2 = dst_rect;
1089 // before fading in, store backbuffer (without animation graphics)
1090 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1091 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1093 // copy source and target surfaces to temporary surfaces for fading
1094 if (fade_mode & FADE_TYPE_TRANSFORM)
1096 // (source and target fading buffer already prepared)
1098 else if (fade_mode & FADE_TYPE_FADE_IN)
1100 // (target fading buffer already prepared)
1101 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1103 else // FADE_TYPE_FADE_OUT
1105 // (source fading buffer already prepared)
1106 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1109 time_current = SDL_GetTicks();
1111 if (fade_delay <= 0)
1113 // immediately draw final target frame without delay
1114 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1118 // when fading without delay, also skip post delay
1122 if (fade_mode == FADE_MODE_MELT)
1124 boolean done = FALSE;
1125 int melt_pixels = 2;
1126 int melt_columns = width / melt_pixels;
1127 int ypos[melt_columns];
1128 int max_steps = height / 8 + 32;
1133 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1135 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1137 ypos[0] = -GetSimpleRandom(16);
1139 for (i = 1 ; i < melt_columns; i++)
1141 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1143 ypos[i] = ypos[i - 1] + r;
1156 time_last = time_current;
1157 time_current = SDL_GetTicks();
1158 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1159 steps_final = MIN(MAX(0, steps), max_steps);
1163 done = (steps_done >= steps_final);
1165 for (i = 0 ; i < melt_columns; i++)
1173 else if (ypos[i] < height)
1178 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1180 if (ypos[i] + dy >= height)
1181 dy = height - ypos[i];
1183 // copy part of (appearing) target surface to upper area
1184 src_rect.x = src_x + i * melt_pixels;
1185 // src_rect.y = src_y + ypos[i];
1187 src_rect.w = melt_pixels;
1189 src_rect.h = ypos[i] + dy;
1191 dst_rect.x = dst_x + i * melt_pixels;
1192 // dst_rect.y = dst_y + ypos[i];
1195 if (steps_done >= steps_final)
1196 SDL_BlitSurface(surface_target, &src_rect,
1197 surface_screen, &dst_rect);
1201 // copy part of (disappearing) source surface to lower area
1202 src_rect.x = src_x + i * melt_pixels;
1204 src_rect.w = melt_pixels;
1205 src_rect.h = height - ypos[i];
1207 dst_rect.x = dst_x + i * melt_pixels;
1208 dst_rect.y = dst_y + ypos[i];
1210 if (steps_done >= steps_final)
1211 SDL_BlitSurface(surface_source, &src_rect,
1212 surface_screen, &dst_rect);
1218 src_rect.x = src_x + i * melt_pixels;
1220 src_rect.w = melt_pixels;
1221 src_rect.h = height;
1223 dst_rect.x = dst_x + i * melt_pixels;
1226 if (steps_done >= steps_final)
1227 SDL_BlitSurface(surface_target, &src_rect,
1228 surface_screen, &dst_rect);
1232 if (steps_done >= steps_final)
1234 if (draw_border_function != NULL)
1235 draw_border_function();
1237 UpdateScreen_WithFrameDelay(&dst_rect2);
1241 else if (fade_mode == FADE_MODE_CURTAIN)
1245 int xx_size = width / 2;
1247 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1249 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1251 for (xx = 0; xx < xx_size;)
1253 time_last = time_current;
1254 time_current = SDL_GetTicks();
1255 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1256 xx_final = MIN(MAX(0, xx), xx_size);
1261 src_rect.h = height;
1266 // draw new (target) image to screen buffer
1267 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1269 if (xx_final < xx_size)
1271 src_rect.w = xx_size - xx_final;
1272 src_rect.h = height;
1274 // draw old (source) image to screen buffer (left side)
1276 src_rect.x = src_x + xx_final;
1279 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1281 // draw old (source) image to screen buffer (right side)
1283 src_rect.x = src_x + xx_size;
1284 dst_rect.x = dst_x + xx_size + xx_final;
1286 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1289 if (draw_border_function != NULL)
1290 draw_border_function();
1292 // only update the region of the screen that is affected from fading
1293 UpdateScreen_WithFrameDelay(&dst_rect2);
1296 else // fading in, fading out or cross-fading
1301 for (alpha = 0.0; alpha < 255.0;)
1303 time_last = time_current;
1304 time_current = SDL_GetTicks();
1305 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1306 alpha_final = MIN(MAX(0, alpha), 255);
1308 // draw existing (source) image to screen buffer
1309 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1311 // draw new (target) image to screen buffer using alpha blending
1312 SDLSetAlpha(surface_target, TRUE, alpha_final);
1313 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1315 if (draw_border_function != NULL)
1316 draw_border_function();
1318 // only update the region of the screen that is affected from fading
1319 UpdateScreen_WithFrameDelay(&dst_rect);
1324 Delay_WithScreenUpdates(post_delay);
1326 // restore function for drawing global masked border
1327 gfx.draw_global_border_function = draw_global_border_function;
1329 // after fading in, restore backbuffer (without animation graphics)
1330 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1331 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1334 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1335 int to_x, int to_y, Uint32 color)
1337 SDL_Surface *surface = dst_bitmap->surface;
1341 swap_numbers(&from_x, &to_x);
1344 swap_numbers(&from_y, &to_y);
1348 rect.w = (to_x - from_x + 1);
1349 rect.h = (to_y - from_y + 1);
1351 SDL_FillRect(surface, &rect, color);
1354 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1355 int to_x, int to_y, Uint32 color)
1357 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1360 #if ENABLE_UNUSED_CODE
1361 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1362 int num_points, Uint32 color)
1367 for (i = 0; i < num_points - 1; i++)
1369 for (x = 0; x < line_width; x++)
1371 for (y = 0; y < line_width; y++)
1373 int dx = x - line_width / 2;
1374 int dy = y - line_width / 2;
1376 if ((x == 0 && y == 0) ||
1377 (x == 0 && y == line_width - 1) ||
1378 (x == line_width - 1 && y == 0) ||
1379 (x == line_width - 1 && y == line_width - 1))
1382 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1383 points[i+1].x + dx, points[i+1].y + dy, color);
1390 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1392 SDL_Surface *surface = src_bitmap->surface;
1394 switch (surface->format->BytesPerPixel)
1396 case 1: // assuming 8-bpp
1398 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1402 case 2: // probably 15-bpp or 16-bpp
1404 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1408 case 3: // slow 24-bpp mode; usually not used
1411 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1415 shift = surface->format->Rshift;
1416 color |= *(pix + shift / 8) >> shift;
1417 shift = surface->format->Gshift;
1418 color |= *(pix + shift / 8) >> shift;
1419 shift = surface->format->Bshift;
1420 color |= *(pix + shift / 8) >> shift;
1426 case 4: // probably 32-bpp
1428 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1437 // ============================================================================
1438 // The following functions were taken from the SGE library
1439 // (SDL Graphics Extension Library) by Anders Lindström
1440 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1441 // ============================================================================
1443 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1445 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1447 switch (surface->format->BytesPerPixel)
1452 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1458 // Probably 15-bpp or 16-bpp
1459 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1465 // Slow 24-bpp mode, usually not used
1469 // Gack - slow, but endian correct
1470 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1471 shift = surface->format->Rshift;
1472 *(pix+shift/8) = color>>shift;
1473 shift = surface->format->Gshift;
1474 *(pix+shift/8) = color>>shift;
1475 shift = surface->format->Bshift;
1476 *(pix+shift/8) = color>>shift;
1483 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1491 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1492 Uint8 R, Uint8 G, Uint8 B)
1494 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1497 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1499 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1502 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1504 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1507 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1512 // Gack - slow, but endian correct
1513 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1514 shift = surface->format->Rshift;
1515 *(pix+shift/8) = color>>shift;
1516 shift = surface->format->Gshift;
1517 *(pix+shift/8) = color>>shift;
1518 shift = surface->format->Bshift;
1519 *(pix+shift/8) = color>>shift;
1522 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1524 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1527 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1529 switch (dest->format->BytesPerPixel)
1532 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1536 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1540 _PutPixel24(dest,x,y,color);
1544 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1550 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1552 if (SDL_MUSTLOCK(surface))
1554 if (SDL_LockSurface(surface) < 0)
1560 _PutPixel(surface, x, y, color);
1562 if (SDL_MUSTLOCK(surface))
1564 SDL_UnlockSurface(surface);
1569 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1570 Uint8 r, Uint8 g, Uint8 b)
1572 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1575 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1577 if (y >= 0 && y <= dest->h - 1)
1579 switch (dest->format->BytesPerPixel)
1582 return y*dest->pitch;
1586 return y*dest->pitch/2;
1590 return y*dest->pitch;
1594 return y*dest->pitch/4;
1602 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1605 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1607 switch (surface->format->BytesPerPixel)
1612 *((Uint8 *)surface->pixels + ypitch + x) = color;
1618 // Probably 15-bpp or 16-bpp
1619 *((Uint16 *)surface->pixels + ypitch + x) = color;
1625 // Slow 24-bpp mode, usually not used
1629 // Gack - slow, but endian correct
1630 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1631 shift = surface->format->Rshift;
1632 *(pix+shift/8) = color>>shift;
1633 shift = surface->format->Gshift;
1634 *(pix+shift/8) = color>>shift;
1635 shift = surface->format->Bshift;
1636 *(pix+shift/8) = color>>shift;
1643 *((Uint32 *)surface->pixels + ypitch + x) = color;
1650 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1655 if (SDL_MUSTLOCK(Surface))
1657 if (SDL_LockSurface(Surface) < 0)
1671 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1675 if (x2 > Surface->w - 1)
1676 x2 = Surface->w - 1;
1683 SDL_FillRect(Surface, &l, Color);
1685 if (SDL_MUSTLOCK(Surface))
1687 SDL_UnlockSurface(Surface);
1691 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1692 Uint8 R, Uint8 G, Uint8 B)
1694 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1697 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1710 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1714 if (x2 > Surface->w - 1)
1715 x2 = Surface->w - 1;
1722 SDL_FillRect(Surface, &l, Color);
1725 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1730 if (SDL_MUSTLOCK(Surface))
1732 if (SDL_LockSurface(Surface) < 0)
1746 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1750 if (y2 > Surface->h - 1)
1751 y2 = Surface->h - 1;
1758 SDL_FillRect(Surface, &l, Color);
1760 if (SDL_MUSTLOCK(Surface))
1762 SDL_UnlockSurface(Surface);
1766 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1767 Uint8 R, Uint8 G, Uint8 B)
1769 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1772 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1785 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1789 if (y2 > Surface->h - 1)
1790 y2 = Surface->h - 1;
1797 SDL_FillRect(Surface, &l, Color);
1801 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1802 Sint16 x2, Sint16 y2, Uint32 Color,
1803 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1806 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1811 sdx = (dx < 0) ? -1 : 1;
1812 sdy = (dy < 0) ? -1 : 1;
1824 for (x = 0; x < dx; x++)
1826 Callback(Surface, px, py, Color);
1840 for (y = 0; y < dy; y++)
1842 Callback(Surface, px, py, Color);
1857 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1858 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1859 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1862 sge_DoLine(Surface, X1, Y1, X2, Y2,
1863 SDL_MapRGB(Surface->format, R, G, B), Callback);
1867 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1870 if (SDL_MUSTLOCK(Surface))
1872 if (SDL_LockSurface(Surface) < 0)
1877 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1879 // unlock the display
1880 if (SDL_MUSTLOCK(Surface))
1882 SDL_UnlockSurface(Surface);
1887 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1888 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1890 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1894 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1896 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1900 // ----------------------------------------------------------------------------
1901 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1902 // ----------------------------------------------------------------------------
1904 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1905 int width, int height, Uint32 color)
1909 for (y = src_y; y < src_y + height; y++)
1911 for (x = src_x; x < src_x + width; x++)
1913 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1915 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1920 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1921 int src_x, int src_y, int width, int height,
1922 int dst_x, int dst_y)
1926 for (y = 0; y < height; y++)
1928 for (x = 0; x < width; x++)
1930 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1932 if (pixel != BLACK_PIXEL)
1933 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1939 // ============================================================================
1940 // The following functions were taken from the SDL_gfx library version 2.0.3
1941 // (Rotozoomer) by Andreas Schiffler
1942 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1943 // ============================================================================
1945 // ----------------------------------------------------------------------------
1948 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1949 // ----------------------------------------------------------------------------
1959 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1962 tColorRGBA *sp, *csp, *dp;
1966 sp = csp = (tColorRGBA *) src->pixels;
1967 dp = (tColorRGBA *) dst->pixels;
1968 dgap = dst->pitch - dst->w * 4;
1970 for (y = 0; y < dst->h; y++)
1974 for (x = 0; x < dst->w; x++)
1976 tColorRGBA *sp0 = sp;
1977 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1978 tColorRGBA *sp00 = &sp0[0];
1979 tColorRGBA *sp01 = &sp0[1];
1980 tColorRGBA *sp10 = &sp1[0];
1981 tColorRGBA *sp11 = &sp1[1];
1984 // create new color pixel from all four source color pixels
1985 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1986 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1987 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1988 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1993 // advance source pointers
1996 // advance destination pointer
2000 // advance source pointer
2001 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2003 // advance destination pointers
2004 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2010 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2012 int x, y, *sax, *say, *csax, *csay;
2014 tColorRGBA *sp, *csp, *csp0, *dp;
2017 // use specialized zoom function when scaling down to exactly half size
2018 if (src->w == 2 * dst->w &&
2019 src->h == 2 * dst->h)
2020 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2023 sx = (float) src->w / (float) dst->w;
2024 sy = (float) src->h / (float) dst->h;
2026 // allocate memory for row increments
2027 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2028 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2030 // precalculate row increments
2031 for (x = 0; x <= dst->w; x++)
2032 *csax++ = (int)(sx * x);
2034 for (y = 0; y <= dst->h; y++)
2035 *csay++ = (int)(sy * y);
2038 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2039 dp = (tColorRGBA *) dst->pixels;
2040 dgap = dst->pitch - dst->w * 4;
2043 for (y = 0; y < dst->h; y++)
2048 for (x = 0; x < dst->w; x++)
2053 // advance source pointers
2057 // advance destination pointer
2061 // advance source pointer
2063 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2065 // advance destination pointers
2066 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2075 // ----------------------------------------------------------------------------
2078 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2079 // ----------------------------------------------------------------------------
2081 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2083 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2084 Uint8 *sp, *dp, *csp;
2088 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2089 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2091 // allocate memory for row increments
2092 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2093 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2095 // precalculate row increments
2098 for (x = 0; x < dst->w; x++)
2101 *csax = (csx >> 16);
2108 for (y = 0; y < dst->h; y++)
2111 *csay = (csy >> 16);
2118 for (x = 0; x < dst->w; x++)
2126 for (y = 0; y < dst->h; y++)
2133 sp = csp = (Uint8 *) src->pixels;
2134 dp = (Uint8 *) dst->pixels;
2135 dgap = dst->pitch - dst->w;
2139 for (y = 0; y < dst->h; y++)
2143 for (x = 0; x < dst->w; x++)
2148 // advance source pointers
2152 // advance destination pointer
2156 // advance source pointer (for row)
2157 csp += ((*csay) * src->pitch);
2160 // advance destination pointers
2170 // ----------------------------------------------------------------------------
2173 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2174 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2175 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2176 // into a 32bit RGBA format on the fly.
2177 // ----------------------------------------------------------------------------
2179 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2181 SDL_Surface *zoom_src = NULL;
2182 SDL_Surface *zoom_dst = NULL;
2183 boolean is_converted = FALSE;
2190 // determine if source surface is 32 bit or 8 bit
2191 is_32bit = (src->format->BitsPerPixel == 32);
2193 if (is_32bit || src->format->BitsPerPixel == 8)
2195 // use source surface 'as is'
2200 // new source surface is 32 bit with a defined RGB ordering
2201 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2202 0x000000ff, 0x0000ff00, 0x00ff0000,
2203 (src->format->Amask ? 0xff000000 : 0));
2204 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2206 is_converted = TRUE;
2209 // allocate surface to completely contain the zoomed surface
2212 // target surface is 32 bit with source RGBA/ABGR ordering
2213 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2214 zoom_src->format->Rmask,
2215 zoom_src->format->Gmask,
2216 zoom_src->format->Bmask,
2217 zoom_src->format->Amask);
2221 // target surface is 8 bit
2222 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2226 // lock source surface
2227 SDL_LockSurface(zoom_src);
2229 // check which kind of surface we have
2232 // call the 32 bit transformation routine to do the zooming
2233 zoomSurfaceRGBA(zoom_src, zoom_dst);
2238 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2239 zoom_dst->format->palette->colors[i] =
2240 zoom_src->format->palette->colors[i];
2241 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2243 // call the 8 bit transformation routine to do the zooming
2244 zoomSurfaceY(zoom_src, zoom_dst);
2247 // unlock source surface
2248 SDL_UnlockSurface(zoom_src);
2250 // free temporary surface
2252 SDL_FreeSurface(zoom_src);
2254 // return destination surface
2258 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2260 SDL_Surface *new_surface;
2262 if (surface == NULL)
2265 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2266 Fail("SDLGetNativeSurface() failed");
2268 // remove alpha channel from native non-transparent surface, if defined
2269 SDLSetAlpha(new_surface, FALSE, 0);
2271 // remove transparent color from native non-transparent surface, if defined
2272 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2277 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2279 Bitmap *dst_bitmap = CreateBitmapStruct();
2280 SDL_Surface *src_surface = src_bitmap->surface_masked;
2281 SDL_Surface *dst_surface;
2283 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2284 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2286 dst_bitmap->width = dst_width;
2287 dst_bitmap->height = dst_height;
2289 // create zoomed temporary surface from source surface
2290 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2292 // create native format destination surface from zoomed temporary surface
2293 SDLSetNativeSurface(&dst_surface);
2295 // set color key for zoomed surface from source surface, if defined
2296 if (SDLHasColorKey(src_surface))
2297 SDLCopyColorKey(src_surface, dst_surface);
2299 // create native non-transparent surface for opaque blitting
2300 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2302 // set native transparent surface for masked blitting
2303 dst_bitmap->surface_masked = dst_surface;
2309 // ============================================================================
2310 // load image to bitmap
2311 // ============================================================================
2313 Bitmap *SDLLoadImage(char *filename)
2315 Bitmap *new_bitmap = CreateBitmapStruct();
2316 SDL_Surface *sdl_image_tmp;
2318 if (program.headless)
2320 // prevent sanity check warnings at later stage
2321 new_bitmap->width = new_bitmap->height = 1;
2326 print_timestamp_init("SDLLoadImage");
2328 print_timestamp_time(getBaseNamePtr(filename));
2330 // load image to temporary surface
2331 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2332 Fail("IMG_Load('%s') failed: %s", getBaseNamePtr(filename), SDL_GetError());
2334 print_timestamp_time("IMG_Load");
2336 UPDATE_BUSY_STATE();
2338 // create native non-transparent surface for current image
2339 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2340 Fail("SDLGetOpaqueSurface() failed");
2342 print_timestamp_time("SDLGetNativeSurface (opaque)");
2344 UPDATE_BUSY_STATE();
2346 // set black pixel to transparent if no alpha channel / transparent color
2347 if (!SDLHasAlpha(sdl_image_tmp) &&
2348 !SDLHasColorKey(sdl_image_tmp))
2349 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2350 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2352 // create native transparent surface for current image
2353 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2354 Fail("SDLGetNativeSurface() failed");
2356 print_timestamp_time("SDLGetNativeSurface (masked)");
2358 UPDATE_BUSY_STATE();
2360 // free temporary surface
2361 SDL_FreeSurface(sdl_image_tmp);
2363 new_bitmap->width = new_bitmap->surface->w;
2364 new_bitmap->height = new_bitmap->surface->h;
2366 print_timestamp_done("SDLLoadImage");
2372 // ----------------------------------------------------------------------------
2373 // custom cursor fuctions
2374 // ----------------------------------------------------------------------------
2376 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2378 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2379 cursor_info->width, cursor_info->height,
2380 cursor_info->hot_x, cursor_info->hot_y);
2383 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2385 static struct MouseCursorInfo *last_cursor_info = NULL;
2386 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2387 static SDL_Cursor *cursor_default = NULL;
2388 static SDL_Cursor *cursor_current = NULL;
2390 // if invoked for the first time, store the SDL default cursor
2391 if (cursor_default == NULL)
2392 cursor_default = SDL_GetCursor();
2394 // only create new cursor if cursor info (custom only) has changed
2395 if (cursor_info != NULL && cursor_info != last_cursor_info)
2397 cursor_current = create_cursor(cursor_info);
2398 last_cursor_info = cursor_info;
2401 // only set new cursor if cursor info (custom or NULL) has changed
2402 if (cursor_info != last_cursor_info2)
2403 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2405 last_cursor_info2 = cursor_info;
2409 // ============================================================================
2411 // ============================================================================
2413 void SDLOpenAudio(void)
2415 if (program.headless)
2418 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2420 Warn("SDL_InitSubSystem() failed: %s", SDL_GetError());
2425 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2426 AUDIO_NUM_CHANNELS_STEREO,
2427 setup.system.audio_fragment_size) < 0)
2429 Warn("Mix_OpenAudio() failed: %s", SDL_GetError());
2434 audio.sound_available = TRUE;
2435 audio.music_available = TRUE;
2436 audio.loops_available = TRUE;
2437 audio.sound_enabled = TRUE;
2439 // set number of available mixer channels
2440 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2441 audio.music_channel = MUSIC_CHANNEL;
2442 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2444 Mixer_InitChannels();
2447 void SDLCloseAudio(void)
2450 Mix_HaltChannel(-1);
2453 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2457 // ============================================================================
2459 // ============================================================================
2461 void SDLWaitEvent(Event *event)
2463 SDL_WaitEvent(event);
2466 void SDLCorrectRawMousePosition(int *x, int *y)
2468 if (sdl_renderer == NULL)
2471 // this corrects the raw mouse position for logical screen size within event
2472 // filters (correction done later by SDL library when handling mouse events)
2475 float scale_x, scale_y;
2477 SDL_RenderGetViewport(sdl_renderer, &viewport);
2478 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2480 *x = (int)(*x / scale_x);
2481 *y = (int)(*y / scale_y);
2488 // ============================================================================
2489 // joystick functions
2490 // ============================================================================
2492 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2493 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2494 static int sdl_js_axis[MAX_PLAYERS][2];
2495 static int sdl_js_button[MAX_PLAYERS][2];
2496 static boolean sdl_is_controller[MAX_PLAYERS];
2498 void SDLClearJoystickState(void)
2502 for (i = 0; i < MAX_PLAYERS; i++)
2504 for (j = 0; j < 2; j++)
2506 sdl_js_axis_raw[i][j] = -1;
2507 sdl_js_axis[i][j] = 0;
2508 sdl_js_button[i][j] = 0;
2513 boolean SDLOpenJoystick(int nr)
2515 if (nr < 0 || nr >= MAX_PLAYERS)
2518 sdl_is_controller[nr] = SDL_IsGameController(nr);
2521 Debug("joystick", "opening joystick %d (%s)",
2522 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2525 if (sdl_is_controller[nr])
2526 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2528 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2530 return (sdl_joystick[nr] != NULL);
2533 void SDLCloseJoystick(int nr)
2535 if (nr < 0 || nr >= MAX_PLAYERS)
2539 Debug("joystick", "closing joystick %d", nr);
2542 if (sdl_is_controller[nr])
2543 SDL_GameControllerClose(sdl_joystick[nr]);
2545 SDL_JoystickClose(sdl_joystick[nr]);
2547 sdl_joystick[nr] = NULL;
2550 boolean SDLCheckJoystickOpened(int nr)
2552 if (nr < 0 || nr >= MAX_PLAYERS)
2555 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2558 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2560 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2561 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2562 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2563 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2565 if (nr < 0 || nr >= MAX_PLAYERS)
2571 // prevent (slightly jittering, but centered) axis A from resetting axis B
2572 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2573 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2576 sdl_js_axis[nr][axis_id] = axis_value;
2577 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2580 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2582 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2583 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2584 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2585 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2586 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2587 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2588 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2589 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2592 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2593 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2594 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2595 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2596 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2597 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2598 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2599 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2601 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2602 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2603 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2604 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2605 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2607 if (nr < 0 || nr >= MAX_PLAYERS)
2610 if (button_id == -1)
2613 sdl_js_button[nr][button_id] = button_state;
2616 void HandleJoystickEvent(Event *event)
2618 // when using joystick, disable overlay touch buttons
2619 runtime.uses_touch_device = FALSE;
2621 switch (event->type)
2623 case SDL_CONTROLLERDEVICEADDED:
2625 Debug("joystick", "SDL_CONTROLLERDEVICEADDED: device %d added",
2626 event->cdevice.which);
2631 case SDL_CONTROLLERDEVICEREMOVED:
2633 Debug("joystick", "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2634 event->cdevice.which);
2639 case SDL_CONTROLLERAXISMOTION:
2641 Debug("joystick", "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2642 event->caxis.which, event->caxis.axis, event->caxis.value);
2644 setJoystickAxis(event->caxis.which,
2646 event->caxis.value);
2649 case SDL_CONTROLLERBUTTONDOWN:
2651 Debug("joystick", "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2652 event->cbutton.which, event->cbutton.button);
2654 setJoystickButton(event->cbutton.which,
2655 event->cbutton.button,
2659 case SDL_CONTROLLERBUTTONUP:
2661 Debug("joystick", "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2662 event->cbutton.which, event->cbutton.button);
2664 setJoystickButton(event->cbutton.which,
2665 event->cbutton.button,
2669 case SDL_JOYAXISMOTION:
2670 if (sdl_is_controller[event->jaxis.which])
2674 Debug("joystick", "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2675 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2677 if (event->jaxis.axis < 4)
2678 setJoystickAxis(event->jaxis.which,
2680 event->jaxis.value);
2683 case SDL_JOYBUTTONDOWN:
2684 if (sdl_is_controller[event->jaxis.which])
2688 Debug("joystick", "SDL_JOYBUTTONDOWN: 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,
2697 case SDL_JOYBUTTONUP:
2698 if (sdl_is_controller[event->jaxis.which])
2702 Debug("joystick", "SDL_JOYBUTTONUP: device %d, button %d",
2703 event->jbutton.which, event->jbutton.button);
2705 if (event->jbutton.button < 4)
2706 setJoystickButton(event->jbutton.which,
2707 event->jbutton.button,
2716 void SDLInitJoysticks(void)
2718 static boolean sdl_joystick_subsystem_initialized = FALSE;
2719 boolean print_warning = !sdl_joystick_subsystem_initialized;
2720 char *mappings_file_base = getPath2(options.conf_directory,
2721 GAMECONTROLLER_BASENAME);
2722 char *mappings_file_user = getPath2(getUserGameDataDir(),
2723 GAMECONTROLLER_BASENAME);
2727 if (!sdl_joystick_subsystem_initialized)
2729 sdl_joystick_subsystem_initialized = TRUE;
2731 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2733 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2734 Fail("SDL_Init() failed: %s", SDL_GetError());
2736 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2738 // the included game controller base mappings should always be found
2739 if (num_mappings == -1)
2740 Warn("no game controller base mappings found");
2743 Debug("joystick", "%d game controller base mapping(s) added",
2747 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2750 // the personal game controller user mappings may or may not be found
2751 if (num_mappings == -1)
2752 Warn("no game controller user mappings found");
2754 Debug("joystick", , "%d game controller user mapping(s) added",
2757 Debug("joystick", "%d joystick(s) found:", SDL_NumJoysticks());
2760 checked_free(mappings_file_base);
2761 checked_free(mappings_file_user);
2764 for (i = 0; i < SDL_NumJoysticks(); i++)
2766 const char *name, *type;
2768 if (SDL_IsGameController(i))
2770 name = SDL_GameControllerNameForIndex(i);
2771 type = "game controller";
2775 name = SDL_JoystickNameForIndex(i);
2779 Debug("joystick", "- joystick %d (%s): '%s'",
2780 i, type, (name ? name : "(Unknown)"));
2785 // assign joysticks from configured to connected joystick for all players
2786 for (i = 0; i < MAX_PLAYERS; i++)
2788 // get configured joystick for this player
2789 char *device_name = setup.input[i].joy.device_name;
2790 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2792 if (joystick_nr >= SDL_NumJoysticks())
2794 if (setup.input[i].use_joystick && print_warning)
2795 Warn("cannot find joystick %d", joystick_nr);
2800 // store configured joystick number for each player
2801 joystick.nr[i] = joystick_nr;
2804 // now open all connected joysticks (regardless if configured or not)
2805 for (i = 0; i < SDL_NumJoysticks(); i++)
2807 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2808 if (SDLCheckJoystickOpened(i))
2809 SDLCloseJoystick(i);
2811 if (SDLOpenJoystick(i))
2812 joystick.status = JOYSTICK_ACTIVATED;
2813 else if (print_warning)
2814 Warn("cannot open joystick %d", i);
2817 SDLClearJoystickState();
2820 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2822 if (nr < 0 || nr >= MAX_PLAYERS)
2826 *x = sdl_js_axis[nr][0];
2828 *y = sdl_js_axis[nr][1];
2831 *b1 = sdl_js_button[nr][0];
2833 *b2 = sdl_js_button[nr][1];
2839 // ============================================================================
2840 // touch input overlay functions
2841 // ============================================================================
2843 #if defined(USE_TOUCH_INPUT_OVERLAY)
2844 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2847 int grid_xsize = overlay.grid_xsize;
2848 int grid_ysize = overlay.grid_ysize;
2851 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2852 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2854 for (x = 0; x < grid_xsize; x++)
2856 rect.x = (x + 0) * video.screen_width / grid_xsize;
2857 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2859 for (y = 0; y < grid_ysize; y++)
2861 rect.y = (y + 0) * video.screen_height / grid_ysize;
2862 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2864 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2865 SDL_RenderDrawRect(sdl_renderer, &rect);
2869 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2872 static void RenderFillRectangle(int x, int y, int width, int height)
2874 SDL_Rect rect = { x, y, width, height };
2876 SDL_RenderFillRect(sdl_renderer, &rect);
2879 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2881 static int alpha_direction = 0;
2882 static int alpha_highlight = 0;
2883 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2884 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2886 int grid_xsize = overlay.grid_xsize;
2887 int grid_ysize = overlay.grid_ysize;
2890 if (alpha == alpha_max)
2892 if (alpha_direction < 0)
2894 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2896 if (alpha_highlight == 0)
2897 alpha_direction = 1;
2901 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2903 if (alpha_highlight == alpha_max)
2904 alpha_direction = -1;
2909 alpha_direction = 1;
2910 alpha_highlight = alpha;
2913 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2915 for (x = 0; x < grid_xsize; x++)
2917 for (y = 0; y < grid_ysize; y++)
2919 int grid_button = overlay.grid_button[x][y];
2920 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2921 int alpha_draw = alpha;
2922 int outline_border = MV_NONE;
2923 int border_size = 2;
2924 boolean draw_outlined = setup.touch.draw_outlined;
2925 boolean draw_pressed = setup.touch.draw_pressed;
2927 if (grid_button == CHAR_GRID_BUTTON_NONE)
2930 if (grid_button == overlay.grid_button_highlight)
2932 draw_outlined = FALSE;
2933 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2936 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2939 draw_outlined = FALSE;
2941 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2944 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2946 rect.x = (x + 0) * video.screen_width / grid_xsize;
2947 rect.y = (y + 0) * video.screen_height / grid_ysize;
2948 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2949 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2951 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2953 rect.x += border_size;
2954 rect.w -= border_size;
2956 outline_border |= MV_LEFT;
2959 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2961 rect.w -= border_size;
2963 outline_border |= MV_RIGHT;
2966 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2968 rect.y += border_size;
2969 rect.h -= border_size;
2971 outline_border |= MV_UP;
2974 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2976 rect.h -= border_size;
2978 outline_border |= MV_DOWN;
2983 int rect_x = rect.x +
2984 (outline_border & MV_LEFT ? border_size : 0);
2985 int rect_w = rect.w -
2986 (outline_border & MV_LEFT ? border_size : 0) -
2987 (outline_border & MV_RIGHT ? border_size : 0);
2989 if (outline_border & MV_LEFT)
2990 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2992 if (outline_border & MV_RIGHT)
2993 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2994 border_size, rect.h);
2996 if (outline_border & MV_UP)
2997 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2999 if (outline_border & MV_DOWN)
3000 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
3001 rect_w, border_size);
3005 SDL_RenderFillRect(sdl_renderer, &rect);
3010 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3013 static void DrawTouchInputOverlay(void)
3015 static boolean deactivated = TRUE;
3016 static boolean show_grid = FALSE;
3017 static int alpha = 0;
3018 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3019 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3020 boolean active = (overlay.enabled && overlay.active);
3022 if (!active && deactivated)
3027 if (alpha < alpha_max)
3028 alpha = MIN(alpha + alpha_step, alpha_max);
3030 deactivated = FALSE;
3034 alpha = MAX(0, alpha - alpha_step);
3040 if (overlay.show_grid)
3042 else if (deactivated)
3046 DrawTouchInputOverlay_ShowGrid(alpha);
3048 DrawTouchInputOverlay_ShowGridButtons(alpha);
3051 static void DrawTouchGadgetsOverlay(void)
3053 DrawGadgets_OverlayTouchButtons();