1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
19 #define ENABLE_UNUSED_CODE 0 // currently unused functions
21 #define DEBUG_JOYSTICKS 0
24 // ============================================================================
26 // ============================================================================
28 // SDL internal variables
29 static SDL_Window *sdl_window = NULL;
30 static SDL_Renderer *sdl_renderer = NULL;
31 static SDL_Texture *sdl_texture_stream = NULL;
32 static SDL_Texture *sdl_texture_target = NULL;
33 static boolean fullscreen_enabled = FALSE;
34 static boolean limit_screen_updates = FALSE;
37 // functions from SGE library
38 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
40 #if defined(USE_TOUCH_INPUT_OVERLAY)
41 // functions to draw overlay graphics for touch device input
42 static void DrawTouchInputOverlay(void);
43 static void DrawTouchGadgetsOverlay(void);
46 void SDLLimitScreenUpdates(boolean enable)
48 limit_screen_updates = enable;
51 static void FinalizeScreen(int draw_target)
53 // copy global animations to render target buffer, if defined (below border)
54 if (gfx.draw_global_anim_function != NULL)
55 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
57 // copy global masked border to render target buffer, if defined
58 if (gfx.draw_global_border_function != NULL)
59 gfx.draw_global_border_function(draw_target);
61 // copy global animations to render target buffer, if defined (above border)
62 if (gfx.draw_global_anim_function != NULL)
63 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
65 // copy tile selection cursor to render target buffer, if defined (above all)
66 if (gfx.draw_tile_cursor_function != NULL)
67 gfx.draw_tile_cursor_function(draw_target);
70 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
72 static unsigned int update_screen_delay = 0;
73 unsigned int update_screen_delay_value = 50; // (milliseconds)
74 SDL_Surface *screen = backbuffer->surface;
76 if (limit_screen_updates &&
77 !DelayReached(&update_screen_delay, update_screen_delay_value))
80 LimitScreenUpdates(FALSE);
84 static int LastFrameCounter = 0;
85 boolean changed = (FrameCounter != LastFrameCounter);
87 Debug("internal:frame", "FrameCounter == %d [%s]", FrameCounter,
88 (changed ? "-" : "SAME FRAME UPDATED"));
90 LastFrameCounter = FrameCounter;
99 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
100 gfx.final_screen_bitmap != NULL) // may not be initialized yet
102 // draw global animations using bitmaps instead of using textures
103 // to prevent texture scaling artefacts (this is potentially slower)
105 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
106 gfx.win_xsize, gfx.win_ysize, 0, 0);
108 FinalizeScreen(DRAW_TO_SCREEN);
110 screen = gfx.final_screen_bitmap->surface;
112 // force full window redraw
116 SDL_Texture *sdl_texture = sdl_texture_stream;
118 // deactivate use of target texture if render targets are not supported
119 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
120 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
121 sdl_texture_target == NULL)
122 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
124 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
125 sdl_texture = sdl_texture_target;
129 int bytes_x = screen->pitch / video.width;
130 int bytes_y = screen->pitch;
132 SDL_UpdateTexture(sdl_texture, rect,
133 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
138 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
141 int xoff = video.screen_xoffset;
142 int yoff = video.screen_yoffset;
143 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
144 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
145 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
147 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
148 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
149 dst_rect2 = &dst_rect_screen;
151 dst_rect1 = &dst_rect_screen;
153 #if defined(HAS_SCREEN_KEYBOARD)
154 if (video.shifted_up || video.shifted_up_delay)
156 int time_current = SDL_GetTicks();
157 int pos = video.shifted_up_pos;
158 int pos_last = video.shifted_up_pos_last;
160 if (!DelayReachedExt(&video.shifted_up_delay, video.shifted_up_delay_value,
163 int delay = time_current - video.shifted_up_delay;
164 int delay_value = video.shifted_up_delay_value;
166 pos = pos_last + (pos - pos_last) * delay / delay_value;
170 video.shifted_up_pos_last = pos;
171 video.shifted_up_delay = 0;
174 SDL_Rect src_rect_up = { 0, pos, video.width, video.height - pos };
175 SDL_Rect dst_rect_up = { xoff, yoff, video.width, video.height - pos };
177 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
178 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
180 src_rect2 = &src_rect_up;
181 dst_rect2 = &dst_rect_up;
185 src_rect1 = &src_rect_up;
186 dst_rect1 = &dst_rect_up;
191 // clear render target buffer
192 SDL_RenderClear(sdl_renderer);
194 // set renderer to use target texture for rendering
195 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
196 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
197 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
199 // copy backbuffer texture to render target buffer
200 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
201 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
203 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
204 FinalizeScreen(DRAW_TO_SCREEN);
206 // when using target texture, copy it to screen buffer
207 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
208 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
210 SDL_SetRenderTarget(sdl_renderer, NULL);
211 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
214 #if defined(USE_TOUCH_INPUT_OVERLAY)
215 // draw overlay graphics for touch device input, if needed
216 DrawTouchInputOverlay();
218 // draw overlay gadgets for touch device input, if needed
219 DrawTouchGadgetsOverlay();
222 // global synchronization point of the game to align video frame delay
223 if (with_frame_delay)
224 WaitUntilDelayReached(&video.frame_delay, video.frame_delay_value);
226 video.frame_counter++;
228 // show render target buffer on screen
229 SDL_RenderPresent(sdl_renderer);
232 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
234 PumpEvents(); // execute event filter actions while waiting
236 UpdateScreenExt(rect, TRUE);
239 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
241 UpdateScreenExt(rect, FALSE);
244 void Delay_WithScreenUpdates(unsigned int delay)
246 unsigned int time_current = SDL_GetTicks();
247 unsigned int time_delayed = time_current + delay;
249 while (time_current < time_delayed)
251 // updating the screen contains waiting for frame delay (non-busy)
252 UpdateScreen_WithFrameDelay(NULL);
254 time_current = SDL_GetTicks();
258 static void SDLSetWindowIcon(char *basename)
260 // (setting the window icon on Mac OS X would replace the high-quality
261 // dock icon with the currently smaller (and uglier) icon from file)
263 #if !defined(PLATFORM_MACOSX)
264 char *filename = getCustomImageFilename(basename);
265 SDL_Surface *surface;
267 if (filename == NULL)
269 Warn("SDLSetWindowIcon(): cannot find file '%s'", basename);
274 if ((surface = IMG_Load(filename)) == NULL)
276 Warn("IMG_Load('%s') failed: %s", basename, SDL_GetError());
281 // set transparent color
282 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
283 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
285 SDL_SetWindowIcon(sdl_window, surface);
289 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
290 SDL_PixelFormat *format2)
292 return (format1->format == format2->format &&
293 format1->BitsPerPixel == format2->BitsPerPixel &&
294 format1->BytesPerPixel == format2->BytesPerPixel &&
295 format1->Rmask == format2->Rmask &&
296 format1->Gmask == format2->Gmask &&
297 format1->Bmask == format2->Bmask);
300 static void SDLCopyColorKey(SDL_Surface *src_surface, SDL_Surface *dst_surface)
305 // check if source surface has a color key
306 if (SDL_GetColorKey(src_surface, &color_key) == 0)
308 // get RGB values of color key of source surface
309 SDL_GetRGB(color_key, src_surface->format, &r, &g, &b);
311 // get color key from RGB values in destination surface format
312 color_key = SDL_MapRGB(dst_surface->format, r, g, b);
314 // set color key in destination surface
315 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL, color_key);
319 // unset color key in destination surface
320 SDL_SetColorKey(dst_surface, UNSET_TRANSPARENT_PIXEL, 0);
324 static boolean SDLHasColorKey(SDL_Surface *surface)
328 return (SDL_GetColorKey(surface, &color_key) == 0);
331 static boolean SDLHasAlpha(SDL_Surface *surface)
333 SDL_BlendMode blend_mode;
335 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
338 return (blend_mode == SDL_BLENDMODE_BLEND);
341 void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
343 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
345 SDL_SetSurfaceBlendMode(surface, blend_mode);
346 SDL_SetSurfaceAlphaMod(surface, alpha);
349 const char *SDLGetRendererName(void)
351 static SDL_RendererInfo renderer_info;
353 SDL_GetRendererInfo(sdl_renderer, &renderer_info);
355 return renderer_info.name;
358 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
360 SDL_PixelFormat format;
361 SDL_Surface *new_surface;
366 if (backbuffer && backbuffer->surface)
368 format = *backbuffer->surface->format;
369 format.Amask = surface->format->Amask; // keep alpha channel
373 format = *surface->format;
376 new_surface = SDL_ConvertSurface(surface, &format, 0);
378 if (new_surface == NULL)
379 Fail("SDL_ConvertSurface() failed: %s", SDL_GetError());
381 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
382 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
383 SDLCopyColorKey(surface, new_surface);
388 boolean SDLSetNativeSurface(SDL_Surface **surface)
390 SDL_Surface *new_surface;
392 if (surface == NULL ||
394 backbuffer == NULL ||
395 backbuffer->surface == NULL)
398 // if pixel format already optimized for destination surface, do nothing
399 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
402 new_surface = SDLGetNativeSurface(*surface);
404 SDL_FreeSurface(*surface);
406 *surface = new_surface;
411 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
413 if (program.headless)
416 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
419 Fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
424 void SDLCreateBitmapTextures(Bitmap *bitmap)
430 SDL_DestroyTexture(bitmap->texture);
431 if (bitmap->texture_masked)
432 SDL_DestroyTexture(bitmap->texture_masked);
434 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
435 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
438 void SDLFreeBitmapTextures(Bitmap *bitmap)
444 SDL_DestroyTexture(bitmap->texture);
445 if (bitmap->texture_masked)
446 SDL_DestroyTexture(bitmap->texture_masked);
448 bitmap->texture = NULL;
449 bitmap->texture_masked = NULL;
452 void SDLInitVideoDisplay(void)
454 // set hint to select render driver as specified in setup config file
455 if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
456 SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
458 // initialize SDL video
459 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
460 Fail("SDL_InitSubSystem() failed: %s", SDL_GetError());
462 // set default SDL depth
463 video.default_depth = 32; // (how to determine video depth in SDL2?)
465 // Code used with SDL 1.2:
466 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
469 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
471 if (program.headless)
474 video.window_scaling_percent = setup.window_scaling_percent;
475 video.window_scaling_quality = setup.window_scaling_quality;
477 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
479 // SDL 2.0: support for (desktop) fullscreen mode available
480 video.fullscreen_available = TRUE;
482 // open SDL video output device (window or fullscreen mode)
483 if (!SDLSetVideoMode(fullscreen))
484 Fail("setting video mode failed");
486 // !!! SDL2 can only set the window icon if the window already exists !!!
488 SDLSetWindowIcon(program.icon_filename);
490 // set window and icon title
494 static void SDLInitVideoBuffer_DrawBuffer(void)
496 /* SDL cannot directly draw to the visible video framebuffer like X11,
497 but always uses a backbuffer, which is then blitted to the visible
498 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
499 visible video framebuffer with 'SDL_Flip', if the hardware supports
500 this). Therefore do not use an additional backbuffer for drawing, but
501 use a symbolic buffer (distinguishable from the SDL backbuffer) called
502 'window', which indicates that the SDL backbuffer should be updated to
503 the visible video framebuffer when attempting to blit to it.
505 For convenience, it seems to be a good idea to create this symbolic
506 buffer 'window' at the same size as the SDL backbuffer. Although it
507 should never be drawn to directly, it would do no harm nevertheless. */
509 // create additional (symbolic) buffer for double-buffering
510 ReCreateBitmap(&window, video.width, video.height);
512 // create dummy drawing buffer for headless mode, if needed
513 if (program.headless)
514 ReCreateBitmap(&backbuffer, video.width, video.height);
517 void SDLInitVideoBuffer(boolean fullscreen)
519 SDLInitVideoBuffer_VideoBuffer(fullscreen);
520 SDLInitVideoBuffer_DrawBuffer();
523 static boolean SDLCreateScreen(boolean fullscreen)
525 SDL_Surface *new_surface = NULL;
527 int surface_flags_window = SURFACE_FLAGS;
528 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
531 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
533 video.vsync_mode = VSYNC_MODE_OFF;
535 if (!strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF))
537 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
538 video.vsync_mode = VSYNC_MODE_NORMAL;
541 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
542 _without_ enabling 2D/3D acceleration and/or guest additions installed,
543 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
544 it will try to use accelerated graphics and apparently fails miserably) */
545 int renderer_flags = SDL_RENDERER_SOFTWARE;
548 int width = video.width;
549 int height = video.height;
550 int screen_width = video.screen_width;
551 int screen_height = video.screen_height;
552 int surface_flags = (fullscreen ? surface_flags_fullscreen :
553 surface_flags_window);
555 // default window size is unscaled
556 video.window_width = screen_width;
557 video.window_height = screen_height;
559 // store if initial screen mode is fullscreen mode when changing screen size
560 video.fullscreen_initial = fullscreen;
562 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
564 video.window_width = window_scaling_factor * screen_width;
565 video.window_height = window_scaling_factor * screen_height;
567 if (sdl_texture_stream)
569 SDL_DestroyTexture(sdl_texture_stream);
570 sdl_texture_stream = NULL;
573 if (sdl_texture_target)
575 SDL_DestroyTexture(sdl_texture_target);
576 sdl_texture_target = NULL;
579 if (!(fullscreen && fullscreen_enabled))
583 SDL_DestroyRenderer(sdl_renderer);
589 SDL_DestroyWindow(sdl_window);
594 if (sdl_window == NULL)
595 sdl_window = SDL_CreateWindow(program.window_title,
596 SDL_WINDOWPOS_CENTERED,
597 SDL_WINDOWPOS_CENTERED,
602 if (sdl_window != NULL)
604 if (sdl_renderer == NULL)
605 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
607 if (sdl_renderer != NULL)
609 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
610 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
612 // required for setting adaptive vsync when using OpenGL renderer
613 SDLSetScreenVsyncMode(setup.vsync_mode);
615 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
616 SDL_PIXELFORMAT_ARGB8888,
617 SDL_TEXTUREACCESS_STREAMING,
620 if (SDL_RenderTargetSupported(sdl_renderer))
621 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
622 SDL_PIXELFORMAT_ARGB8888,
623 SDL_TEXTUREACCESS_TARGET,
626 if (sdl_texture_stream != NULL)
628 // use SDL default values for RGB masks and no alpha channel
629 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
631 if (new_surface == NULL)
632 Warn("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
636 Warn("SDL_CreateTexture() failed: %s", SDL_GetError());
641 Warn("SDL_CreateRenderer() failed: %s", SDL_GetError());
646 Warn("SDL_CreateWindow() failed: %s", SDL_GetError());
649 SDLSetScreenProperties();
651 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
652 if (new_surface != NULL)
653 fullscreen_enabled = fullscreen;
655 if (backbuffer == NULL)
656 backbuffer = CreateBitmapStruct();
658 backbuffer->width = video.width;
659 backbuffer->height = video.height;
661 if (backbuffer->surface)
662 SDL_FreeSurface(backbuffer->surface);
664 backbuffer->surface = new_surface;
666 return (new_surface != NULL);
669 boolean SDLSetVideoMode(boolean fullscreen)
671 boolean success = FALSE;
675 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
677 // switch display to fullscreen mode, if available
678 success = SDLCreateScreen(TRUE);
682 // switching display to fullscreen mode failed -- do not try it again
683 video.fullscreen_available = FALSE;
687 video.fullscreen_enabled = TRUE;
691 if ((!fullscreen && video.fullscreen_enabled) || !success)
693 // switch display to window mode
694 success = SDLCreateScreen(FALSE);
698 // switching display to window mode failed -- should not happen
702 video.fullscreen_enabled = FALSE;
703 video.window_scaling_percent = setup.window_scaling_percent;
704 video.window_scaling_quality = setup.window_scaling_quality;
706 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
710 SDLRedrawWindow(); // map window
715 void SDLSetWindowTitle(void)
717 if (sdl_window == NULL)
720 SDL_SetWindowTitle(sdl_window, program.window_title);
723 void SDLSetWindowScaling(int window_scaling_percent)
725 if (sdl_window == NULL)
728 float window_scaling_factor = (float)window_scaling_percent / 100;
729 int new_window_width = (int)(window_scaling_factor * video.screen_width);
730 int new_window_height = (int)(window_scaling_factor * video.screen_height);
732 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
734 video.window_scaling_percent = window_scaling_percent;
735 video.window_width = new_window_width;
736 video.window_height = new_window_height;
741 void SDLSetWindowScalingQuality(char *window_scaling_quality)
743 SDL_Texture *new_texture;
745 if (sdl_texture_stream == NULL)
748 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
750 new_texture = SDL_CreateTexture(sdl_renderer,
751 SDL_PIXELFORMAT_ARGB8888,
752 SDL_TEXTUREACCESS_STREAMING,
753 video.width, video.height);
755 if (new_texture != NULL)
757 SDL_DestroyTexture(sdl_texture_stream);
759 sdl_texture_stream = new_texture;
762 if (SDL_RenderTargetSupported(sdl_renderer))
763 new_texture = SDL_CreateTexture(sdl_renderer,
764 SDL_PIXELFORMAT_ARGB8888,
765 SDL_TEXTUREACCESS_TARGET,
766 video.width, video.height);
770 if (new_texture != NULL)
772 SDL_DestroyTexture(sdl_texture_target);
774 sdl_texture_target = new_texture;
779 video.window_scaling_quality = window_scaling_quality;
782 void SDLSetWindowFullscreen(boolean fullscreen)
784 if (sdl_window == NULL)
787 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
789 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
790 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
792 // if screen size was changed in fullscreen mode, correct desktop window size
793 if (!fullscreen && video.fullscreen_initial)
795 SDLSetWindowScaling(setup.window_scaling_percent);
796 SDL_SetWindowPosition(sdl_window,
797 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
799 video.fullscreen_initial = FALSE;
803 void SDLSetDisplaySize(void)
805 if (sdl_renderer != NULL)
809 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
811 video.display_width = w;
812 video.display_height = h;
815 Debug("video", "SDL renderer size: %d x %d",
816 video.display_width, video.display_height);
821 SDL_Rect display_bounds;
823 SDL_GetDisplayBounds(0, &display_bounds);
825 video.display_width = display_bounds.w;
826 video.display_height = display_bounds.h;
829 Debug("video", "SDL display size: %d x %d",
830 video.display_width, video.display_height);
835 void SDLSetScreenSizeAndOffsets(int width, int height)
837 // set default video screen size and offsets
838 video.screen_width = width;
839 video.screen_height = height;
840 video.screen_xoffset = 0;
841 video.screen_yoffset = 0;
843 #if defined(USE_COMPLETE_DISPLAY)
844 float ratio_video = (float) width / height;
845 float ratio_display = (float) video.display_width / video.display_height;
847 if (ratio_video != ratio_display)
849 // adjust drawable screen size to cover the whole device display
851 if (ratio_video < ratio_display)
852 video.screen_width *= ratio_display / ratio_video;
854 video.screen_height *= ratio_video / ratio_display;
856 video.screen_xoffset = (video.screen_width - width) / 2;
857 video.screen_yoffset = (video.screen_height - height) / 2;
860 Debug("video", "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
862 video.screen_width, video.screen_height,
863 ratio_video, ratio_display);
869 void SDLSetScreenSizeForRenderer(int width, int height)
871 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
874 void SDLSetScreenProperties(void)
877 SDLSetScreenSizeAndOffsets(video.width, video.height);
878 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
880 SetOverlayGridSizeAndButtons();
883 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
885 video.screen_rendering_mode =
886 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
887 SPECIAL_RENDERING_BITMAP :
888 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
889 SPECIAL_RENDERING_TARGET:
890 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
891 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
894 void SDLSetScreenVsyncMode(char *vsync_mode)
896 // changing vsync mode without re-creating renderer only supported by OpenGL
897 if (!strPrefixLower((char *)SDLGetRendererName(), "opengl"))
900 int interval = VSYNC_MODE_STR_TO_INT(vsync_mode);
901 int result = SDL_GL_SetSwapInterval(interval);
903 // if adaptive vsync requested, but not supported, retry with normal vsync
904 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
906 interval = VSYNC_MODE_NORMAL;
908 result = SDL_GL_SetSwapInterval(interval);
912 interval = VSYNC_MODE_OFF;
914 video.vsync_mode = interval;
917 void SDLRedrawWindow(void)
919 UpdateScreen_WithoutFrameDelay(NULL);
922 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
925 if (program.headless)
928 SDL_Surface *surface =
929 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
932 Fail("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
934 SDLSetNativeSurface(&surface);
936 bitmap->surface = surface;
939 void SDLFreeBitmapPointers(Bitmap *bitmap)
942 SDL_FreeSurface(bitmap->surface);
943 if (bitmap->surface_masked)
944 SDL_FreeSurface(bitmap->surface_masked);
946 bitmap->surface = NULL;
947 bitmap->surface_masked = NULL;
950 SDL_DestroyTexture(bitmap->texture);
951 if (bitmap->texture_masked)
952 SDL_DestroyTexture(bitmap->texture_masked);
954 bitmap->texture = NULL;
955 bitmap->texture_masked = NULL;
958 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
959 int src_x, int src_y, int width, int height,
960 int dst_x, int dst_y, int mask_mode)
962 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
963 SDL_Rect src_rect, dst_rect;
975 // if (src_bitmap != backbuffer || dst_bitmap != window)
976 if (!(src_bitmap == backbuffer && dst_bitmap == window))
977 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
978 src_bitmap->surface_masked : src_bitmap->surface),
979 &src_rect, real_dst_bitmap->surface, &dst_rect);
981 if (dst_bitmap == window)
982 UpdateScreen_WithFrameDelay(&dst_rect);
985 void SDLBlitTexture(Bitmap *bitmap,
986 int src_x, int src_y, int width, int height,
987 int dst_x, int dst_y, int mask_mode)
989 SDL_Texture *texture;
994 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1002 src_rect.h = height;
1007 dst_rect.h = height;
1009 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1012 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1015 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1023 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1025 if (dst_bitmap == window)
1026 UpdateScreen_WithFrameDelay(&rect);
1029 void PrepareFadeBitmap(int draw_target)
1031 Bitmap *fade_bitmap =
1032 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1033 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1035 if (fade_bitmap == NULL)
1038 // copy backbuffer to fading buffer
1039 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1041 // add border and animations to fading buffer
1042 FinalizeScreen(draw_target);
1045 void SDLFadeRectangle(int x, int y, int width, int height,
1046 int fade_mode, int fade_delay, int post_delay,
1047 void (*draw_border_function)(void))
1049 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1050 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1051 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1052 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1053 SDL_Surface *surface_screen = backbuffer->surface;
1054 SDL_Rect src_rect, dst_rect;
1056 int src_x = x, src_y = y;
1057 int dst_x = x, dst_y = y;
1058 unsigned int time_last, time_current;
1060 // store function for drawing global masked border
1061 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1063 // deactivate drawing of global border while fading, if needed
1064 if (draw_border_function == NULL)
1065 gfx.draw_global_border_function = NULL;
1070 src_rect.h = height;
1074 dst_rect.w = width; // (ignored)
1075 dst_rect.h = height; // (ignored)
1077 dst_rect2 = dst_rect;
1079 // before fading in, store backbuffer (without animation graphics)
1080 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1081 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1083 // copy source and target surfaces to temporary surfaces for fading
1084 if (fade_mode & FADE_TYPE_TRANSFORM)
1086 // (source and target fading buffer already prepared)
1088 else if (fade_mode & FADE_TYPE_FADE_IN)
1090 // (target fading buffer already prepared)
1091 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1093 else // FADE_TYPE_FADE_OUT
1095 // (source fading buffer already prepared)
1096 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1099 time_current = SDL_GetTicks();
1101 if (fade_delay <= 0)
1103 // immediately draw final target frame without delay
1104 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1108 // when fading without delay, also skip post delay
1112 if (fade_mode == FADE_MODE_MELT)
1114 boolean done = FALSE;
1115 int melt_pixels = 2;
1116 int melt_columns = width / melt_pixels;
1117 int ypos[melt_columns];
1118 int max_steps = height / 8 + 32;
1123 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1125 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1127 ypos[0] = -GetSimpleRandom(16);
1129 for (i = 1 ; i < melt_columns; i++)
1131 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1133 ypos[i] = ypos[i - 1] + r;
1146 time_last = time_current;
1147 time_current = SDL_GetTicks();
1148 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1149 steps_final = MIN(MAX(0, steps), max_steps);
1153 done = (steps_done >= steps_final);
1155 for (i = 0 ; i < melt_columns; i++)
1163 else if (ypos[i] < height)
1168 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1170 if (ypos[i] + dy >= height)
1171 dy = height - ypos[i];
1173 // copy part of (appearing) target surface to upper area
1174 src_rect.x = src_x + i * melt_pixels;
1175 // src_rect.y = src_y + ypos[i];
1177 src_rect.w = melt_pixels;
1179 src_rect.h = ypos[i] + dy;
1181 dst_rect.x = dst_x + i * melt_pixels;
1182 // dst_rect.y = dst_y + ypos[i];
1185 if (steps_done >= steps_final)
1186 SDL_BlitSurface(surface_target, &src_rect,
1187 surface_screen, &dst_rect);
1191 // copy part of (disappearing) source surface to lower area
1192 src_rect.x = src_x + i * melt_pixels;
1194 src_rect.w = melt_pixels;
1195 src_rect.h = height - ypos[i];
1197 dst_rect.x = dst_x + i * melt_pixels;
1198 dst_rect.y = dst_y + ypos[i];
1200 if (steps_done >= steps_final)
1201 SDL_BlitSurface(surface_source, &src_rect,
1202 surface_screen, &dst_rect);
1208 src_rect.x = src_x + i * melt_pixels;
1210 src_rect.w = melt_pixels;
1211 src_rect.h = height;
1213 dst_rect.x = dst_x + i * melt_pixels;
1216 if (steps_done >= steps_final)
1217 SDL_BlitSurface(surface_target, &src_rect,
1218 surface_screen, &dst_rect);
1222 if (steps_done >= steps_final)
1224 if (draw_border_function != NULL)
1225 draw_border_function();
1227 UpdateScreen_WithFrameDelay(&dst_rect2);
1229 if (PendingEscapeKeyEvent())
1234 else if (fade_mode == FADE_MODE_CURTAIN)
1238 int xx_size = width / 2;
1240 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1242 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1244 for (xx = 0; xx < xx_size;)
1246 time_last = time_current;
1247 time_current = SDL_GetTicks();
1248 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1249 xx_final = MIN(MAX(0, xx), xx_size);
1254 src_rect.h = height;
1259 // draw new (target) image to screen buffer
1260 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1262 if (xx_final < xx_size)
1264 src_rect.w = xx_size - xx_final;
1265 src_rect.h = height;
1267 // draw old (source) image to screen buffer (left side)
1269 src_rect.x = src_x + xx_final;
1272 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1274 // draw old (source) image to screen buffer (right side)
1276 src_rect.x = src_x + xx_size;
1277 dst_rect.x = dst_x + xx_size + xx_final;
1279 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1282 if (draw_border_function != NULL)
1283 draw_border_function();
1285 // only update the region of the screen that is affected from fading
1286 UpdateScreen_WithFrameDelay(&dst_rect2);
1288 if (PendingEscapeKeyEvent())
1292 else // fading in, fading out or cross-fading
1297 for (alpha = 0.0; alpha < 255.0;)
1299 time_last = time_current;
1300 time_current = SDL_GetTicks();
1301 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1302 alpha_final = MIN(MAX(0, alpha), 255);
1304 // draw existing (source) image to screen buffer
1305 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1307 // draw new (target) image to screen buffer using alpha blending
1308 SDLSetAlpha(surface_target, TRUE, alpha_final);
1309 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1311 if (draw_border_function != NULL)
1312 draw_border_function();
1314 // only update the region of the screen that is affected from fading
1315 UpdateScreen_WithFrameDelay(&dst_rect);
1317 if (PendingEscapeKeyEvent())
1323 Delay_WithScreenUpdates(post_delay);
1325 // restore function for drawing global masked border
1326 gfx.draw_global_border_function = draw_global_border_function;
1328 // after fading in, restore backbuffer (without animation graphics)
1329 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1330 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1333 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1334 int to_x, int to_y, Uint32 color)
1336 SDL_Surface *surface = dst_bitmap->surface;
1340 swap_numbers(&from_x, &to_x);
1343 swap_numbers(&from_y, &to_y);
1347 rect.w = (to_x - from_x + 1);
1348 rect.h = (to_y - from_y + 1);
1350 SDL_FillRect(surface, &rect, color);
1353 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1354 int to_x, int to_y, Uint32 color)
1356 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1359 #if ENABLE_UNUSED_CODE
1360 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1361 int num_points, Uint32 color)
1366 for (i = 0; i < num_points - 1; i++)
1368 for (x = 0; x < line_width; x++)
1370 for (y = 0; y < line_width; y++)
1372 int dx = x - line_width / 2;
1373 int dy = y - line_width / 2;
1375 if ((x == 0 && y == 0) ||
1376 (x == 0 && y == line_width - 1) ||
1377 (x == line_width - 1 && y == 0) ||
1378 (x == line_width - 1 && y == line_width - 1))
1381 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1382 points[i+1].x + dx, points[i+1].y + dy, color);
1389 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1391 SDL_Surface *surface = src_bitmap->surface;
1393 switch (surface->format->BytesPerPixel)
1395 case 1: // assuming 8-bpp
1397 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1401 case 2: // probably 15-bpp or 16-bpp
1403 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1407 case 3: // slow 24-bpp mode; usually not used
1410 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1414 shift = surface->format->Rshift;
1415 color |= *(pix + shift / 8) >> shift;
1416 shift = surface->format->Gshift;
1417 color |= *(pix + shift / 8) >> shift;
1418 shift = surface->format->Bshift;
1419 color |= *(pix + shift / 8) >> shift;
1425 case 4: // probably 32-bpp
1427 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1436 // ============================================================================
1437 // The following functions were taken from the SGE library
1438 // (SDL Graphics Extension Library) by Anders Lindström
1439 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1440 // ============================================================================
1442 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1444 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1446 switch (surface->format->BytesPerPixel)
1451 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1457 // Probably 15-bpp or 16-bpp
1458 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1464 // Slow 24-bpp mode, usually not used
1468 // Gack - slow, but endian correct
1469 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1470 shift = surface->format->Rshift;
1471 *(pix+shift/8) = color>>shift;
1472 shift = surface->format->Gshift;
1473 *(pix+shift/8) = color>>shift;
1474 shift = surface->format->Bshift;
1475 *(pix+shift/8) = color>>shift;
1482 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1490 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1491 Uint8 R, Uint8 G, Uint8 B)
1493 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1496 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1498 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1501 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1503 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1506 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1511 // Gack - slow, but endian correct
1512 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1513 shift = surface->format->Rshift;
1514 *(pix+shift/8) = color>>shift;
1515 shift = surface->format->Gshift;
1516 *(pix+shift/8) = color>>shift;
1517 shift = surface->format->Bshift;
1518 *(pix+shift/8) = color>>shift;
1521 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1523 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1526 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1528 switch (dest->format->BytesPerPixel)
1531 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1535 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1539 _PutPixel24(dest,x,y,color);
1543 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1549 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1551 if (SDL_MUSTLOCK(surface))
1553 if (SDL_LockSurface(surface) < 0)
1559 _PutPixel(surface, x, y, color);
1561 if (SDL_MUSTLOCK(surface))
1563 SDL_UnlockSurface(surface);
1568 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1569 Uint8 r, Uint8 g, Uint8 b)
1571 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1574 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1576 if (y >= 0 && y <= dest->h - 1)
1578 switch (dest->format->BytesPerPixel)
1581 return y*dest->pitch;
1585 return y*dest->pitch/2;
1589 return y*dest->pitch;
1593 return y*dest->pitch/4;
1601 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1604 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1606 switch (surface->format->BytesPerPixel)
1611 *((Uint8 *)surface->pixels + ypitch + x) = color;
1617 // Probably 15-bpp or 16-bpp
1618 *((Uint16 *)surface->pixels + ypitch + x) = color;
1624 // Slow 24-bpp mode, usually not used
1628 // Gack - slow, but endian correct
1629 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1630 shift = surface->format->Rshift;
1631 *(pix+shift/8) = color>>shift;
1632 shift = surface->format->Gshift;
1633 *(pix+shift/8) = color>>shift;
1634 shift = surface->format->Bshift;
1635 *(pix+shift/8) = color>>shift;
1642 *((Uint32 *)surface->pixels + ypitch + x) = color;
1649 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1654 if (SDL_MUSTLOCK(Surface))
1656 if (SDL_LockSurface(Surface) < 0)
1670 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1674 if (x2 > Surface->w - 1)
1675 x2 = Surface->w - 1;
1682 SDL_FillRect(Surface, &l, Color);
1684 if (SDL_MUSTLOCK(Surface))
1686 SDL_UnlockSurface(Surface);
1690 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1691 Uint8 R, Uint8 G, Uint8 B)
1693 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1696 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1709 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1713 if (x2 > Surface->w - 1)
1714 x2 = Surface->w - 1;
1721 SDL_FillRect(Surface, &l, Color);
1724 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1729 if (SDL_MUSTLOCK(Surface))
1731 if (SDL_LockSurface(Surface) < 0)
1745 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1749 if (y2 > Surface->h - 1)
1750 y2 = Surface->h - 1;
1757 SDL_FillRect(Surface, &l, Color);
1759 if (SDL_MUSTLOCK(Surface))
1761 SDL_UnlockSurface(Surface);
1765 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1766 Uint8 R, Uint8 G, Uint8 B)
1768 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1771 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1784 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1788 if (y2 > Surface->h - 1)
1789 y2 = Surface->h - 1;
1796 SDL_FillRect(Surface, &l, Color);
1800 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1801 Sint16 x2, Sint16 y2, Uint32 Color,
1802 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1805 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1810 sdx = (dx < 0) ? -1 : 1;
1811 sdy = (dy < 0) ? -1 : 1;
1823 for (x = 0; x < dx; x++)
1825 Callback(Surface, px, py, Color);
1839 for (y = 0; y < dy; y++)
1841 Callback(Surface, px, py, Color);
1856 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1857 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1858 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1861 sge_DoLine(Surface, X1, Y1, X2, Y2,
1862 SDL_MapRGB(Surface->format, R, G, B), Callback);
1866 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1869 if (SDL_MUSTLOCK(Surface))
1871 if (SDL_LockSurface(Surface) < 0)
1876 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1878 // unlock the display
1879 if (SDL_MUSTLOCK(Surface))
1881 SDL_UnlockSurface(Surface);
1886 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1887 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1889 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1893 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1895 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1899 // ----------------------------------------------------------------------------
1900 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1901 // ----------------------------------------------------------------------------
1903 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1904 int width, int height, Uint32 color)
1908 for (y = src_y; y < src_y + height; y++)
1910 for (x = src_x; x < src_x + width; x++)
1912 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1914 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1919 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1920 int src_x, int src_y, int width, int height,
1921 int dst_x, int dst_y)
1925 for (y = 0; y < height; y++)
1927 for (x = 0; x < width; x++)
1929 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1931 if (pixel != BLACK_PIXEL)
1932 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1938 // ============================================================================
1939 // The following functions were taken from the SDL_gfx library version 2.0.3
1940 // (Rotozoomer) by Andreas Schiffler
1941 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1942 // ============================================================================
1944 // ----------------------------------------------------------------------------
1947 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1948 // ----------------------------------------------------------------------------
1958 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1961 tColorRGBA *sp, *csp, *dp;
1965 sp = csp = (tColorRGBA *) src->pixels;
1966 dp = (tColorRGBA *) dst->pixels;
1967 dgap = dst->pitch - dst->w * 4;
1969 for (y = 0; y < dst->h; y++)
1973 for (x = 0; x < dst->w; x++)
1975 tColorRGBA *sp0 = sp;
1976 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1977 tColorRGBA *sp00 = &sp0[0];
1978 tColorRGBA *sp01 = &sp0[1];
1979 tColorRGBA *sp10 = &sp1[0];
1980 tColorRGBA *sp11 = &sp1[1];
1983 // create new color pixel from all four source color pixels
1984 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1985 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1986 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1987 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1992 // advance source pointers
1995 // advance destination pointer
1999 // advance source pointer
2000 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2002 // advance destination pointers
2003 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2009 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2011 int x, y, *sax, *say, *csax, *csay;
2013 tColorRGBA *sp, *csp, *csp0, *dp;
2016 // use specialized zoom function when scaling down to exactly half size
2017 if (src->w == 2 * dst->w &&
2018 src->h == 2 * dst->h)
2019 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2022 sx = (float) src->w / (float) dst->w;
2023 sy = (float) src->h / (float) dst->h;
2025 // allocate memory for row increments
2026 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2027 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2029 // precalculate row increments
2030 for (x = 0; x <= dst->w; x++)
2031 *csax++ = (int)(sx * x);
2033 for (y = 0; y <= dst->h; y++)
2034 *csay++ = (int)(sy * y);
2037 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2038 dp = (tColorRGBA *) dst->pixels;
2039 dgap = dst->pitch - dst->w * 4;
2042 for (y = 0; y < dst->h; y++)
2047 for (x = 0; x < dst->w; x++)
2052 // advance source pointers
2056 // advance destination pointer
2060 // advance source pointer
2062 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2064 // advance destination pointers
2065 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2074 // ----------------------------------------------------------------------------
2077 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2078 // ----------------------------------------------------------------------------
2080 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2082 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2083 Uint8 *sp, *dp, *csp;
2087 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2088 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2090 // allocate memory for row increments
2091 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2092 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2094 // precalculate row increments
2097 for (x = 0; x < dst->w; x++)
2100 *csax = (csx >> 16);
2107 for (y = 0; y < dst->h; y++)
2110 *csay = (csy >> 16);
2117 for (x = 0; x < dst->w; x++)
2125 for (y = 0; y < dst->h; y++)
2132 sp = csp = (Uint8 *) src->pixels;
2133 dp = (Uint8 *) dst->pixels;
2134 dgap = dst->pitch - dst->w;
2138 for (y = 0; y < dst->h; y++)
2142 for (x = 0; x < dst->w; x++)
2147 // advance source pointers
2151 // advance destination pointer
2155 // advance source pointer (for row)
2156 csp += ((*csay) * src->pitch);
2159 // advance destination pointers
2169 // ----------------------------------------------------------------------------
2172 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2173 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2174 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2175 // into a 32bit RGBA format on the fly.
2176 // ----------------------------------------------------------------------------
2178 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2180 SDL_Surface *zoom_src = NULL;
2181 SDL_Surface *zoom_dst = NULL;
2182 boolean is_converted = FALSE;
2189 // determine if source surface is 32 bit or 8 bit
2190 is_32bit = (src->format->BitsPerPixel == 32);
2192 if (is_32bit || src->format->BitsPerPixel == 8)
2194 // use source surface 'as is'
2199 // new source surface is 32 bit with a defined RGB ordering
2200 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2201 0x000000ff, 0x0000ff00, 0x00ff0000,
2202 (src->format->Amask ? 0xff000000 : 0));
2203 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2205 is_converted = TRUE;
2208 // allocate surface to completely contain the zoomed surface
2211 // target surface is 32 bit with source RGBA/ABGR ordering
2212 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2213 zoom_src->format->Rmask,
2214 zoom_src->format->Gmask,
2215 zoom_src->format->Bmask,
2216 zoom_src->format->Amask);
2220 // target surface is 8 bit
2221 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2225 // lock source surface
2226 SDL_LockSurface(zoom_src);
2228 // check which kind of surface we have
2231 // call the 32 bit transformation routine to do the zooming
2232 zoomSurfaceRGBA(zoom_src, zoom_dst);
2237 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2238 zoom_dst->format->palette->colors[i] =
2239 zoom_src->format->palette->colors[i];
2240 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2242 // call the 8 bit transformation routine to do the zooming
2243 zoomSurfaceY(zoom_src, zoom_dst);
2246 // unlock source surface
2247 SDL_UnlockSurface(zoom_src);
2249 // free temporary surface
2251 SDL_FreeSurface(zoom_src);
2253 // return destination surface
2257 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2259 SDL_Surface *new_surface;
2261 if (surface == NULL)
2264 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2265 Fail("SDLGetNativeSurface() failed");
2267 // remove alpha channel from native non-transparent surface, if defined
2268 SDLSetAlpha(new_surface, FALSE, 0);
2270 // remove transparent color from native non-transparent surface, if defined
2271 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2276 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2278 Bitmap *dst_bitmap = CreateBitmapStruct();
2279 SDL_Surface *src_surface = src_bitmap->surface_masked;
2280 SDL_Surface *dst_surface;
2282 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2283 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2285 dst_bitmap->width = dst_width;
2286 dst_bitmap->height = dst_height;
2288 // create zoomed temporary surface from source surface
2289 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2291 // create native format destination surface from zoomed temporary surface
2292 SDLSetNativeSurface(&dst_surface);
2294 // set color key for zoomed surface from source surface, if defined
2295 if (SDLHasColorKey(src_surface))
2296 SDLCopyColorKey(src_surface, dst_surface);
2298 // create native non-transparent surface for opaque blitting
2299 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2301 // set native transparent surface for masked blitting
2302 dst_bitmap->surface_masked = dst_surface;
2308 // ============================================================================
2309 // load image to bitmap
2310 // ============================================================================
2312 Bitmap *SDLLoadImage(char *filename)
2314 Bitmap *new_bitmap = CreateBitmapStruct();
2315 SDL_Surface *sdl_image_tmp;
2317 if (program.headless)
2319 // prevent sanity check warnings at later stage
2320 new_bitmap->width = new_bitmap->height = 1;
2325 print_timestamp_init("SDLLoadImage");
2327 print_timestamp_time(getBaseNamePtr(filename));
2329 // load image to temporary surface
2330 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2331 Fail("IMG_Load('%s') failed: %s", getBaseNamePtr(filename), SDL_GetError());
2333 print_timestamp_time("IMG_Load");
2335 UPDATE_BUSY_STATE();
2337 // create native non-transparent surface for current image
2338 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2339 Fail("SDLGetOpaqueSurface() failed");
2341 print_timestamp_time("SDLGetNativeSurface (opaque)");
2343 UPDATE_BUSY_STATE();
2345 // set black pixel to transparent if no alpha channel / transparent color
2346 if (!SDLHasAlpha(sdl_image_tmp) &&
2347 !SDLHasColorKey(sdl_image_tmp))
2348 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2349 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2351 // create native transparent surface for current image
2352 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2353 Fail("SDLGetNativeSurface() failed");
2355 print_timestamp_time("SDLGetNativeSurface (masked)");
2357 UPDATE_BUSY_STATE();
2359 // free temporary surface
2360 SDL_FreeSurface(sdl_image_tmp);
2362 new_bitmap->width = new_bitmap->surface->w;
2363 new_bitmap->height = new_bitmap->surface->h;
2365 print_timestamp_done("SDLLoadImage");
2371 // ----------------------------------------------------------------------------
2372 // custom cursor fuctions
2373 // ----------------------------------------------------------------------------
2375 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2377 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2378 cursor_info->width, cursor_info->height,
2379 cursor_info->hot_x, cursor_info->hot_y);
2382 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2384 static struct MouseCursorInfo *last_cursor_info = NULL;
2385 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2386 static SDL_Cursor *cursor_default = NULL;
2387 static SDL_Cursor *cursor_current = NULL;
2389 // if invoked for the first time, store the SDL default cursor
2390 if (cursor_default == NULL)
2391 cursor_default = SDL_GetCursor();
2393 // only create new cursor if cursor info (custom only) has changed
2394 if (cursor_info != NULL && cursor_info != last_cursor_info)
2396 cursor_current = create_cursor(cursor_info);
2397 last_cursor_info = cursor_info;
2400 // only set new cursor if cursor info (custom or NULL) has changed
2401 if (cursor_info != last_cursor_info2)
2402 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2404 last_cursor_info2 = cursor_info;
2408 // ============================================================================
2410 // ============================================================================
2412 void SDLOpenAudio(void)
2414 if (program.headless)
2417 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2419 Warn("SDL_InitSubSystem() failed: %s", SDL_GetError());
2424 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2425 AUDIO_NUM_CHANNELS_STEREO,
2426 setup.system.audio_fragment_size) < 0)
2428 Warn("Mix_OpenAudio() failed: %s", SDL_GetError());
2433 audio.sound_available = TRUE;
2434 audio.music_available = TRUE;
2435 audio.loops_available = TRUE;
2436 audio.sound_enabled = TRUE;
2438 // set number of available mixer channels
2439 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2440 audio.music_channel = MUSIC_CHANNEL;
2441 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2443 Mixer_InitChannels();
2446 void SDLCloseAudio(void)
2449 Mix_HaltChannel(-1);
2452 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2456 // ============================================================================
2458 // ============================================================================
2460 void SDLWaitEvent(Event *event)
2462 SDL_WaitEvent(event);
2465 void SDLCorrectRawMousePosition(int *x, int *y)
2467 if (sdl_renderer == NULL)
2470 // this corrects the raw mouse position for logical screen size within event
2471 // filters (correction done later by SDL library when handling mouse events)
2474 float scale_x, scale_y;
2476 SDL_RenderGetViewport(sdl_renderer, &viewport);
2477 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2479 *x = (int)(*x / scale_x);
2480 *y = (int)(*y / scale_y);
2487 // ============================================================================
2488 // joystick functions
2489 // ============================================================================
2491 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2492 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2493 static int sdl_js_axis[MAX_PLAYERS][2];
2494 static int sdl_js_button[MAX_PLAYERS][2];
2495 static boolean sdl_is_controller[MAX_PLAYERS];
2497 void SDLClearJoystickState(void)
2501 for (i = 0; i < MAX_PLAYERS; i++)
2503 for (j = 0; j < 2; j++)
2505 sdl_js_axis_raw[i][j] = -1;
2506 sdl_js_axis[i][j] = 0;
2507 sdl_js_button[i][j] = 0;
2512 boolean SDLOpenJoystick(int nr)
2514 if (nr < 0 || nr >= MAX_PLAYERS)
2517 sdl_is_controller[nr] = SDL_IsGameController(nr);
2520 Debug("joystick", "opening joystick %d (%s)",
2521 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2524 if (sdl_is_controller[nr])
2525 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2527 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2529 return (sdl_joystick[nr] != NULL);
2532 void SDLCloseJoystick(int nr)
2534 if (nr < 0 || nr >= MAX_PLAYERS)
2538 Debug("joystick", "closing joystick %d", nr);
2541 if (sdl_is_controller[nr])
2542 SDL_GameControllerClose(sdl_joystick[nr]);
2544 SDL_JoystickClose(sdl_joystick[nr]);
2546 sdl_joystick[nr] = NULL;
2549 boolean SDLCheckJoystickOpened(int nr)
2551 if (nr < 0 || nr >= MAX_PLAYERS)
2554 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2557 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2559 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2560 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2561 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2562 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2564 if (nr < 0 || nr >= MAX_PLAYERS)
2570 // prevent (slightly jittering, but centered) axis A from resetting axis B
2571 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2572 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2575 sdl_js_axis[nr][axis_id] = axis_value;
2576 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2579 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2581 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2582 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2583 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2584 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2585 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2586 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2587 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2588 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2591 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2592 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2593 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2594 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2595 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2596 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2597 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2598 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2600 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2601 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2602 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2603 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2604 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2606 if (nr < 0 || nr >= MAX_PLAYERS)
2609 if (button_id == -1)
2612 sdl_js_button[nr][button_id] = button_state;
2615 void HandleJoystickEvent(Event *event)
2617 // when using joystick, disable overlay touch buttons
2618 runtime.uses_touch_device = FALSE;
2620 switch (event->type)
2622 case SDL_CONTROLLERDEVICEADDED:
2624 Debug("joystick", "SDL_CONTROLLERDEVICEADDED: device %d added",
2625 event->cdevice.which);
2630 case SDL_CONTROLLERDEVICEREMOVED:
2632 Debug("joystick", "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2633 event->cdevice.which);
2638 case SDL_CONTROLLERAXISMOTION:
2640 Debug("joystick", "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2641 event->caxis.which, event->caxis.axis, event->caxis.value);
2643 setJoystickAxis(event->caxis.which,
2645 event->caxis.value);
2648 case SDL_CONTROLLERBUTTONDOWN:
2650 Debug("joystick", "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2651 event->cbutton.which, event->cbutton.button);
2653 setJoystickButton(event->cbutton.which,
2654 event->cbutton.button,
2658 case SDL_CONTROLLERBUTTONUP:
2660 Debug("joystick", "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2661 event->cbutton.which, event->cbutton.button);
2663 setJoystickButton(event->cbutton.which,
2664 event->cbutton.button,
2668 case SDL_JOYAXISMOTION:
2669 if (sdl_is_controller[event->jaxis.which])
2673 Debug("joystick", "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2674 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2676 if (event->jaxis.axis < 4)
2677 setJoystickAxis(event->jaxis.which,
2679 event->jaxis.value);
2682 case SDL_JOYBUTTONDOWN:
2683 if (sdl_is_controller[event->jaxis.which])
2687 Debug("joystick", "SDL_JOYBUTTONDOWN: device %d, button %d",
2688 event->jbutton.which, event->jbutton.button);
2690 if (event->jbutton.button < 4)
2691 setJoystickButton(event->jbutton.which,
2692 event->jbutton.button,
2696 case SDL_JOYBUTTONUP:
2697 if (sdl_is_controller[event->jaxis.which])
2701 Debug("joystick", "SDL_JOYBUTTONUP: device %d, button %d",
2702 event->jbutton.which, event->jbutton.button);
2704 if (event->jbutton.button < 4)
2705 setJoystickButton(event->jbutton.which,
2706 event->jbutton.button,
2715 void SDLInitJoysticks(void)
2717 static boolean sdl_joystick_subsystem_initialized = FALSE;
2718 boolean print_warning = !sdl_joystick_subsystem_initialized;
2719 char *mappings_file_base = getPath2(options.conf_directory,
2720 GAMECONTROLLER_BASENAME);
2721 char *mappings_file_user = getPath2(getMainUserGameDataDir(),
2722 GAMECONTROLLER_BASENAME);
2726 if (!sdl_joystick_subsystem_initialized)
2728 sdl_joystick_subsystem_initialized = TRUE;
2730 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2732 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2733 Fail("SDL_Init() failed: %s", SDL_GetError());
2735 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2737 // the included game controller base mappings should always be found
2738 if (num_mappings == -1)
2739 Warn("no game controller base mappings found");
2742 Debug("joystick", "%d game controller base mapping(s) added",
2746 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2749 // the personal game controller user mappings may or may not be found
2750 if (num_mappings == -1)
2751 Warn("no game controller user mappings found");
2753 Debug("joystick", , "%d game controller user mapping(s) added",
2756 Debug("joystick", "%d joystick(s) found:", SDL_NumJoysticks());
2759 checked_free(mappings_file_base);
2760 checked_free(mappings_file_user);
2763 for (i = 0; i < SDL_NumJoysticks(); i++)
2765 const char *name, *type;
2767 if (SDL_IsGameController(i))
2769 name = SDL_GameControllerNameForIndex(i);
2770 type = "game controller";
2774 name = SDL_JoystickNameForIndex(i);
2778 Debug("joystick", "- joystick %d (%s): '%s'",
2779 i, type, (name ? name : "(Unknown)"));
2784 // assign joysticks from configured to connected joystick for all players
2785 for (i = 0; i < MAX_PLAYERS; i++)
2787 // get configured joystick for this player
2788 char *device_name = setup.input[i].joy.device_name;
2789 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2791 if (joystick_nr >= SDL_NumJoysticks())
2793 if (setup.input[i].use_joystick && print_warning)
2794 Warn("cannot find joystick %d", joystick_nr);
2799 // store configured joystick number for each player
2800 joystick.nr[i] = joystick_nr;
2803 // now open all connected joysticks (regardless if configured or not)
2804 for (i = 0; i < SDL_NumJoysticks(); i++)
2806 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2807 if (SDLCheckJoystickOpened(i))
2808 SDLCloseJoystick(i);
2810 if (SDLOpenJoystick(i))
2811 joystick.status = JOYSTICK_ACTIVATED;
2812 else if (print_warning)
2813 Warn("cannot open joystick %d", i);
2816 SDLClearJoystickState();
2819 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2821 if (nr < 0 || nr >= MAX_PLAYERS)
2825 *x = sdl_js_axis[nr][0];
2827 *y = sdl_js_axis[nr][1];
2830 *b1 = sdl_js_button[nr][0];
2832 *b2 = sdl_js_button[nr][1];
2838 // ============================================================================
2839 // touch input overlay functions
2840 // ============================================================================
2842 #if defined(USE_TOUCH_INPUT_OVERLAY)
2843 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2846 int grid_xsize = overlay.grid_xsize;
2847 int grid_ysize = overlay.grid_ysize;
2850 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2851 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2853 for (x = 0; x < grid_xsize; x++)
2855 rect.x = (x + 0) * video.screen_width / grid_xsize;
2856 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2858 for (y = 0; y < grid_ysize; y++)
2860 rect.y = (y + 0) * video.screen_height / grid_ysize;
2861 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2863 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2864 SDL_RenderDrawRect(sdl_renderer, &rect);
2868 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2871 static void RenderFillRectangle(int x, int y, int width, int height)
2873 SDL_Rect rect = { x, y, width, height };
2875 SDL_RenderFillRect(sdl_renderer, &rect);
2878 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2880 static int alpha_direction = 0;
2881 static int alpha_highlight = 0;
2882 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2883 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2885 int grid_xsize = overlay.grid_xsize;
2886 int grid_ysize = overlay.grid_ysize;
2889 if (alpha == alpha_max)
2891 if (alpha_direction < 0)
2893 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2895 if (alpha_highlight == 0)
2896 alpha_direction = 1;
2900 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2902 if (alpha_highlight == alpha_max)
2903 alpha_direction = -1;
2908 alpha_direction = 1;
2909 alpha_highlight = alpha;
2912 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2914 for (x = 0; x < grid_xsize; x++)
2916 for (y = 0; y < grid_ysize; y++)
2918 int grid_button = overlay.grid_button[x][y];
2919 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2920 int alpha_draw = alpha;
2921 int outline_border = MV_NONE;
2922 int border_size = 2;
2923 boolean draw_outlined = setup.touch.draw_outlined;
2924 boolean draw_pressed = setup.touch.draw_pressed;
2926 if (grid_button == CHAR_GRID_BUTTON_NONE)
2929 if (grid_button == overlay.grid_button_highlight)
2931 draw_outlined = FALSE;
2932 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2935 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2938 draw_outlined = FALSE;
2940 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2943 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2945 rect.x = (x + 0) * video.screen_width / grid_xsize;
2946 rect.y = (y + 0) * video.screen_height / grid_ysize;
2947 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2948 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2950 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2952 rect.x += border_size;
2953 rect.w -= border_size;
2955 outline_border |= MV_LEFT;
2958 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2960 rect.w -= border_size;
2962 outline_border |= MV_RIGHT;
2965 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2967 rect.y += border_size;
2968 rect.h -= border_size;
2970 outline_border |= MV_UP;
2973 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2975 rect.h -= border_size;
2977 outline_border |= MV_DOWN;
2982 int rect_x = rect.x +
2983 (outline_border & MV_LEFT ? border_size : 0);
2984 int rect_w = rect.w -
2985 (outline_border & MV_LEFT ? border_size : 0) -
2986 (outline_border & MV_RIGHT ? border_size : 0);
2988 if (outline_border & MV_LEFT)
2989 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2991 if (outline_border & MV_RIGHT)
2992 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2993 border_size, rect.h);
2995 if (outline_border & MV_UP)
2996 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2998 if (outline_border & MV_DOWN)
2999 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
3000 rect_w, border_size);
3004 SDL_RenderFillRect(sdl_renderer, &rect);
3009 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3012 static void DrawTouchInputOverlay(void)
3014 static boolean deactivated = TRUE;
3015 static boolean show_grid = FALSE;
3016 static int alpha = 0;
3017 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3018 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3019 boolean active = (overlay.enabled && overlay.active);
3021 if (!active && deactivated)
3026 if (alpha < alpha_max)
3027 alpha = MIN(alpha + alpha_step, alpha_max);
3029 deactivated = FALSE;
3033 alpha = MAX(0, alpha - alpha_step);
3039 if (overlay.show_grid)
3041 else if (deactivated)
3045 DrawTouchInputOverlay_ShowGrid(alpha);
3047 DrawTouchInputOverlay_ShowGridButtons(alpha);
3050 static void DrawTouchGadgetsOverlay(void)
3052 DrawGadgets_OverlayTouchButtons();