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);
1231 else if (fade_mode == FADE_MODE_CURTAIN)
1235 int xx_size = width / 2;
1237 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1239 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1241 for (xx = 0; xx < xx_size;)
1243 time_last = time_current;
1244 time_current = SDL_GetTicks();
1245 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1246 xx_final = MIN(MAX(0, xx), xx_size);
1251 src_rect.h = height;
1256 // draw new (target) image to screen buffer
1257 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1259 if (xx_final < xx_size)
1261 src_rect.w = xx_size - xx_final;
1262 src_rect.h = height;
1264 // draw old (source) image to screen buffer (left side)
1266 src_rect.x = src_x + xx_final;
1269 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1271 // draw old (source) image to screen buffer (right side)
1273 src_rect.x = src_x + xx_size;
1274 dst_rect.x = dst_x + xx_size + xx_final;
1276 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1279 if (draw_border_function != NULL)
1280 draw_border_function();
1282 // only update the region of the screen that is affected from fading
1283 UpdateScreen_WithFrameDelay(&dst_rect2);
1286 else // fading in, fading out or cross-fading
1291 for (alpha = 0.0; alpha < 255.0;)
1293 time_last = time_current;
1294 time_current = SDL_GetTicks();
1295 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1296 alpha_final = MIN(MAX(0, alpha), 255);
1298 // draw existing (source) image to screen buffer
1299 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1301 // draw new (target) image to screen buffer using alpha blending
1302 SDLSetAlpha(surface_target, TRUE, alpha_final);
1303 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1305 if (draw_border_function != NULL)
1306 draw_border_function();
1308 // only update the region of the screen that is affected from fading
1309 UpdateScreen_WithFrameDelay(&dst_rect);
1314 Delay_WithScreenUpdates(post_delay);
1316 // restore function for drawing global masked border
1317 gfx.draw_global_border_function = draw_global_border_function;
1319 // after fading in, restore backbuffer (without animation graphics)
1320 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1321 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1324 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1325 int to_x, int to_y, Uint32 color)
1327 SDL_Surface *surface = dst_bitmap->surface;
1331 swap_numbers(&from_x, &to_x);
1334 swap_numbers(&from_y, &to_y);
1338 rect.w = (to_x - from_x + 1);
1339 rect.h = (to_y - from_y + 1);
1341 SDL_FillRect(surface, &rect, color);
1344 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1345 int to_x, int to_y, Uint32 color)
1347 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1350 #if ENABLE_UNUSED_CODE
1351 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1352 int num_points, Uint32 color)
1357 for (i = 0; i < num_points - 1; i++)
1359 for (x = 0; x < line_width; x++)
1361 for (y = 0; y < line_width; y++)
1363 int dx = x - line_width / 2;
1364 int dy = y - line_width / 2;
1366 if ((x == 0 && y == 0) ||
1367 (x == 0 && y == line_width - 1) ||
1368 (x == line_width - 1 && y == 0) ||
1369 (x == line_width - 1 && y == line_width - 1))
1372 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1373 points[i+1].x + dx, points[i+1].y + dy, color);
1380 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1382 SDL_Surface *surface = src_bitmap->surface;
1384 switch (surface->format->BytesPerPixel)
1386 case 1: // assuming 8-bpp
1388 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1392 case 2: // probably 15-bpp or 16-bpp
1394 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1398 case 3: // slow 24-bpp mode; usually not used
1401 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1405 shift = surface->format->Rshift;
1406 color |= *(pix + shift / 8) >> shift;
1407 shift = surface->format->Gshift;
1408 color |= *(pix + shift / 8) >> shift;
1409 shift = surface->format->Bshift;
1410 color |= *(pix + shift / 8) >> shift;
1416 case 4: // probably 32-bpp
1418 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1427 // ============================================================================
1428 // The following functions were taken from the SGE library
1429 // (SDL Graphics Extension Library) by Anders Lindström
1430 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1431 // ============================================================================
1433 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1435 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1437 switch (surface->format->BytesPerPixel)
1442 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1448 // Probably 15-bpp or 16-bpp
1449 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1455 // Slow 24-bpp mode, usually not used
1459 // Gack - slow, but endian correct
1460 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1461 shift = surface->format->Rshift;
1462 *(pix+shift/8) = color>>shift;
1463 shift = surface->format->Gshift;
1464 *(pix+shift/8) = color>>shift;
1465 shift = surface->format->Bshift;
1466 *(pix+shift/8) = color>>shift;
1473 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1481 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1482 Uint8 R, Uint8 G, Uint8 B)
1484 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1487 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1489 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1492 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1494 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1497 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1502 // Gack - slow, but endian correct
1503 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1504 shift = surface->format->Rshift;
1505 *(pix+shift/8) = color>>shift;
1506 shift = surface->format->Gshift;
1507 *(pix+shift/8) = color>>shift;
1508 shift = surface->format->Bshift;
1509 *(pix+shift/8) = color>>shift;
1512 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1514 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1517 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1519 switch (dest->format->BytesPerPixel)
1522 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1526 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1530 _PutPixel24(dest,x,y,color);
1534 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1540 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1542 if (SDL_MUSTLOCK(surface))
1544 if (SDL_LockSurface(surface) < 0)
1550 _PutPixel(surface, x, y, color);
1552 if (SDL_MUSTLOCK(surface))
1554 SDL_UnlockSurface(surface);
1559 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1560 Uint8 r, Uint8 g, Uint8 b)
1562 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1565 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1567 if (y >= 0 && y <= dest->h - 1)
1569 switch (dest->format->BytesPerPixel)
1572 return y*dest->pitch;
1576 return y*dest->pitch/2;
1580 return y*dest->pitch;
1584 return y*dest->pitch/4;
1592 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1595 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1597 switch (surface->format->BytesPerPixel)
1602 *((Uint8 *)surface->pixels + ypitch + x) = color;
1608 // Probably 15-bpp or 16-bpp
1609 *((Uint16 *)surface->pixels + ypitch + x) = color;
1615 // Slow 24-bpp mode, usually not used
1619 // Gack - slow, but endian correct
1620 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1621 shift = surface->format->Rshift;
1622 *(pix+shift/8) = color>>shift;
1623 shift = surface->format->Gshift;
1624 *(pix+shift/8) = color>>shift;
1625 shift = surface->format->Bshift;
1626 *(pix+shift/8) = color>>shift;
1633 *((Uint32 *)surface->pixels + ypitch + x) = color;
1640 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1645 if (SDL_MUSTLOCK(Surface))
1647 if (SDL_LockSurface(Surface) < 0)
1661 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1665 if (x2 > Surface->w - 1)
1666 x2 = Surface->w - 1;
1673 SDL_FillRect(Surface, &l, Color);
1675 if (SDL_MUSTLOCK(Surface))
1677 SDL_UnlockSurface(Surface);
1681 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1682 Uint8 R, Uint8 G, Uint8 B)
1684 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1687 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1700 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1704 if (x2 > Surface->w - 1)
1705 x2 = Surface->w - 1;
1712 SDL_FillRect(Surface, &l, Color);
1715 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1720 if (SDL_MUSTLOCK(Surface))
1722 if (SDL_LockSurface(Surface) < 0)
1736 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1740 if (y2 > Surface->h - 1)
1741 y2 = Surface->h - 1;
1748 SDL_FillRect(Surface, &l, Color);
1750 if (SDL_MUSTLOCK(Surface))
1752 SDL_UnlockSurface(Surface);
1756 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1757 Uint8 R, Uint8 G, Uint8 B)
1759 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1762 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1775 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1779 if (y2 > Surface->h - 1)
1780 y2 = Surface->h - 1;
1787 SDL_FillRect(Surface, &l, Color);
1791 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1792 Sint16 x2, Sint16 y2, Uint32 Color,
1793 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1796 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1801 sdx = (dx < 0) ? -1 : 1;
1802 sdy = (dy < 0) ? -1 : 1;
1814 for (x = 0; x < dx; x++)
1816 Callback(Surface, px, py, Color);
1830 for (y = 0; y < dy; y++)
1832 Callback(Surface, px, py, Color);
1847 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1848 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1849 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1852 sge_DoLine(Surface, X1, Y1, X2, Y2,
1853 SDL_MapRGB(Surface->format, R, G, B), Callback);
1857 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1860 if (SDL_MUSTLOCK(Surface))
1862 if (SDL_LockSurface(Surface) < 0)
1867 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1869 // unlock the display
1870 if (SDL_MUSTLOCK(Surface))
1872 SDL_UnlockSurface(Surface);
1877 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1878 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1880 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1884 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1886 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1890 // ----------------------------------------------------------------------------
1891 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1892 // ----------------------------------------------------------------------------
1894 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1895 int width, int height, Uint32 color)
1899 for (y = src_y; y < src_y + height; y++)
1901 for (x = src_x; x < src_x + width; x++)
1903 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1905 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1910 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1911 int src_x, int src_y, int width, int height,
1912 int dst_x, int dst_y)
1916 for (y = 0; y < height; y++)
1918 for (x = 0; x < width; x++)
1920 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1922 if (pixel != BLACK_PIXEL)
1923 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1929 // ============================================================================
1930 // The following functions were taken from the SDL_gfx library version 2.0.3
1931 // (Rotozoomer) by Andreas Schiffler
1932 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1933 // ============================================================================
1935 // ----------------------------------------------------------------------------
1938 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1939 // ----------------------------------------------------------------------------
1949 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1952 tColorRGBA *sp, *csp, *dp;
1956 sp = csp = (tColorRGBA *) src->pixels;
1957 dp = (tColorRGBA *) dst->pixels;
1958 dgap = dst->pitch - dst->w * 4;
1960 for (y = 0; y < dst->h; y++)
1964 for (x = 0; x < dst->w; x++)
1966 tColorRGBA *sp0 = sp;
1967 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1968 tColorRGBA *sp00 = &sp0[0];
1969 tColorRGBA *sp01 = &sp0[1];
1970 tColorRGBA *sp10 = &sp1[0];
1971 tColorRGBA *sp11 = &sp1[1];
1974 // create new color pixel from all four source color pixels
1975 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1976 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1977 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1978 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1983 // advance source pointers
1986 // advance destination pointer
1990 // advance source pointer
1991 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1993 // advance destination pointers
1994 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2000 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2002 int x, y, *sax, *say, *csax, *csay;
2004 tColorRGBA *sp, *csp, *csp0, *dp;
2007 // use specialized zoom function when scaling down to exactly half size
2008 if (src->w == 2 * dst->w &&
2009 src->h == 2 * dst->h)
2010 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2013 sx = (float) src->w / (float) dst->w;
2014 sy = (float) src->h / (float) dst->h;
2016 // allocate memory for row increments
2017 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2018 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2020 // precalculate row increments
2021 for (x = 0; x <= dst->w; x++)
2022 *csax++ = (int)(sx * x);
2024 for (y = 0; y <= dst->h; y++)
2025 *csay++ = (int)(sy * y);
2028 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2029 dp = (tColorRGBA *) dst->pixels;
2030 dgap = dst->pitch - dst->w * 4;
2033 for (y = 0; y < dst->h; y++)
2038 for (x = 0; x < dst->w; x++)
2043 // advance source pointers
2047 // advance destination pointer
2051 // advance source pointer
2053 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2055 // advance destination pointers
2056 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2065 // ----------------------------------------------------------------------------
2068 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2069 // ----------------------------------------------------------------------------
2071 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2073 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2074 Uint8 *sp, *dp, *csp;
2078 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2079 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2081 // allocate memory for row increments
2082 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2083 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2085 // precalculate row increments
2088 for (x = 0; x < dst->w; x++)
2091 *csax = (csx >> 16);
2098 for (y = 0; y < dst->h; y++)
2101 *csay = (csy >> 16);
2108 for (x = 0; x < dst->w; x++)
2116 for (y = 0; y < dst->h; y++)
2123 sp = csp = (Uint8 *) src->pixels;
2124 dp = (Uint8 *) dst->pixels;
2125 dgap = dst->pitch - dst->w;
2129 for (y = 0; y < dst->h; y++)
2133 for (x = 0; x < dst->w; x++)
2138 // advance source pointers
2142 // advance destination pointer
2146 // advance source pointer (for row)
2147 csp += ((*csay) * src->pitch);
2150 // advance destination pointers
2160 // ----------------------------------------------------------------------------
2163 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2164 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2165 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2166 // into a 32bit RGBA format on the fly.
2167 // ----------------------------------------------------------------------------
2169 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2171 SDL_Surface *zoom_src = NULL;
2172 SDL_Surface *zoom_dst = NULL;
2173 boolean is_converted = FALSE;
2180 // determine if source surface is 32 bit or 8 bit
2181 is_32bit = (src->format->BitsPerPixel == 32);
2183 if (is_32bit || src->format->BitsPerPixel == 8)
2185 // use source surface 'as is'
2190 // new source surface is 32 bit with a defined RGB ordering
2191 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2192 0x000000ff, 0x0000ff00, 0x00ff0000,
2193 (src->format->Amask ? 0xff000000 : 0));
2194 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2196 is_converted = TRUE;
2199 // allocate surface to completely contain the zoomed surface
2202 // target surface is 32 bit with source RGBA/ABGR ordering
2203 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2204 zoom_src->format->Rmask,
2205 zoom_src->format->Gmask,
2206 zoom_src->format->Bmask,
2207 zoom_src->format->Amask);
2211 // target surface is 8 bit
2212 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2216 // lock source surface
2217 SDL_LockSurface(zoom_src);
2219 // check which kind of surface we have
2222 // call the 32 bit transformation routine to do the zooming
2223 zoomSurfaceRGBA(zoom_src, zoom_dst);
2228 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2229 zoom_dst->format->palette->colors[i] =
2230 zoom_src->format->palette->colors[i];
2231 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2233 // call the 8 bit transformation routine to do the zooming
2234 zoomSurfaceY(zoom_src, zoom_dst);
2237 // unlock source surface
2238 SDL_UnlockSurface(zoom_src);
2240 // free temporary surface
2242 SDL_FreeSurface(zoom_src);
2244 // return destination surface
2248 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2250 SDL_Surface *new_surface;
2252 if (surface == NULL)
2255 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2256 Fail("SDLGetNativeSurface() failed");
2258 // remove alpha channel from native non-transparent surface, if defined
2259 SDLSetAlpha(new_surface, FALSE, 0);
2261 // remove transparent color from native non-transparent surface, if defined
2262 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2267 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2269 Bitmap *dst_bitmap = CreateBitmapStruct();
2270 SDL_Surface *src_surface = src_bitmap->surface_masked;
2271 SDL_Surface *dst_surface;
2273 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2274 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2276 dst_bitmap->width = dst_width;
2277 dst_bitmap->height = dst_height;
2279 // create zoomed temporary surface from source surface
2280 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2282 // create native format destination surface from zoomed temporary surface
2283 SDLSetNativeSurface(&dst_surface);
2285 // set color key for zoomed surface from source surface, if defined
2286 if (SDLHasColorKey(src_surface))
2287 SDLCopyColorKey(src_surface, dst_surface);
2289 // create native non-transparent surface for opaque blitting
2290 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2292 // set native transparent surface for masked blitting
2293 dst_bitmap->surface_masked = dst_surface;
2299 // ============================================================================
2300 // load image to bitmap
2301 // ============================================================================
2303 Bitmap *SDLLoadImage(char *filename)
2305 Bitmap *new_bitmap = CreateBitmapStruct();
2306 SDL_Surface *sdl_image_tmp;
2308 if (program.headless)
2310 // prevent sanity check warnings at later stage
2311 new_bitmap->width = new_bitmap->height = 1;
2316 print_timestamp_init("SDLLoadImage");
2318 print_timestamp_time(getBaseNamePtr(filename));
2320 // load image to temporary surface
2321 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2322 Fail("IMG_Load('%s') failed: %s", getBaseNamePtr(filename), SDL_GetError());
2324 print_timestamp_time("IMG_Load");
2326 UPDATE_BUSY_STATE();
2328 // create native non-transparent surface for current image
2329 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2330 Fail("SDLGetOpaqueSurface() failed");
2332 print_timestamp_time("SDLGetNativeSurface (opaque)");
2334 UPDATE_BUSY_STATE();
2336 // set black pixel to transparent if no alpha channel / transparent color
2337 if (!SDLHasAlpha(sdl_image_tmp) &&
2338 !SDLHasColorKey(sdl_image_tmp))
2339 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2340 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2342 // create native transparent surface for current image
2343 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2344 Fail("SDLGetNativeSurface() failed");
2346 print_timestamp_time("SDLGetNativeSurface (masked)");
2348 UPDATE_BUSY_STATE();
2350 // free temporary surface
2351 SDL_FreeSurface(sdl_image_tmp);
2353 new_bitmap->width = new_bitmap->surface->w;
2354 new_bitmap->height = new_bitmap->surface->h;
2356 print_timestamp_done("SDLLoadImage");
2362 // ----------------------------------------------------------------------------
2363 // custom cursor fuctions
2364 // ----------------------------------------------------------------------------
2366 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2368 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2369 cursor_info->width, cursor_info->height,
2370 cursor_info->hot_x, cursor_info->hot_y);
2373 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2375 static struct MouseCursorInfo *last_cursor_info = NULL;
2376 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2377 static SDL_Cursor *cursor_default = NULL;
2378 static SDL_Cursor *cursor_current = NULL;
2380 // if invoked for the first time, store the SDL default cursor
2381 if (cursor_default == NULL)
2382 cursor_default = SDL_GetCursor();
2384 // only create new cursor if cursor info (custom only) has changed
2385 if (cursor_info != NULL && cursor_info != last_cursor_info)
2387 cursor_current = create_cursor(cursor_info);
2388 last_cursor_info = cursor_info;
2391 // only set new cursor if cursor info (custom or NULL) has changed
2392 if (cursor_info != last_cursor_info2)
2393 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2395 last_cursor_info2 = cursor_info;
2399 // ============================================================================
2401 // ============================================================================
2403 void SDLOpenAudio(void)
2405 if (program.headless)
2408 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2410 Warn("SDL_InitSubSystem() failed: %s", SDL_GetError());
2415 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2416 AUDIO_NUM_CHANNELS_STEREO,
2417 setup.system.audio_fragment_size) < 0)
2419 Warn("Mix_OpenAudio() failed: %s", SDL_GetError());
2424 audio.sound_available = TRUE;
2425 audio.music_available = TRUE;
2426 audio.loops_available = TRUE;
2427 audio.sound_enabled = TRUE;
2429 // set number of available mixer channels
2430 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2431 audio.music_channel = MUSIC_CHANNEL;
2432 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2434 Mixer_InitChannels();
2437 void SDLCloseAudio(void)
2440 Mix_HaltChannel(-1);
2443 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2447 // ============================================================================
2449 // ============================================================================
2451 void SDLWaitEvent(Event *event)
2453 SDL_WaitEvent(event);
2456 void SDLCorrectRawMousePosition(int *x, int *y)
2458 if (sdl_renderer == NULL)
2461 // this corrects the raw mouse position for logical screen size within event
2462 // filters (correction done later by SDL library when handling mouse events)
2465 float scale_x, scale_y;
2467 SDL_RenderGetViewport(sdl_renderer, &viewport);
2468 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2470 *x = (int)(*x / scale_x);
2471 *y = (int)(*y / scale_y);
2478 // ============================================================================
2479 // joystick functions
2480 // ============================================================================
2482 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2483 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2484 static int sdl_js_axis[MAX_PLAYERS][2];
2485 static int sdl_js_button[MAX_PLAYERS][2];
2486 static boolean sdl_is_controller[MAX_PLAYERS];
2488 void SDLClearJoystickState(void)
2492 for (i = 0; i < MAX_PLAYERS; i++)
2494 for (j = 0; j < 2; j++)
2496 sdl_js_axis_raw[i][j] = -1;
2497 sdl_js_axis[i][j] = 0;
2498 sdl_js_button[i][j] = 0;
2503 boolean SDLOpenJoystick(int nr)
2505 if (nr < 0 || nr >= MAX_PLAYERS)
2508 sdl_is_controller[nr] = SDL_IsGameController(nr);
2511 Debug("joystick", "opening joystick %d (%s)",
2512 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2515 if (sdl_is_controller[nr])
2516 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2518 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2520 return (sdl_joystick[nr] != NULL);
2523 void SDLCloseJoystick(int nr)
2525 if (nr < 0 || nr >= MAX_PLAYERS)
2529 Debug("joystick", "closing joystick %d", nr);
2532 if (sdl_is_controller[nr])
2533 SDL_GameControllerClose(sdl_joystick[nr]);
2535 SDL_JoystickClose(sdl_joystick[nr]);
2537 sdl_joystick[nr] = NULL;
2540 boolean SDLCheckJoystickOpened(int nr)
2542 if (nr < 0 || nr >= MAX_PLAYERS)
2545 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2548 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2550 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2551 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2552 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2553 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2555 if (nr < 0 || nr >= MAX_PLAYERS)
2561 // prevent (slightly jittering, but centered) axis A from resetting axis B
2562 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2563 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2566 sdl_js_axis[nr][axis_id] = axis_value;
2567 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2570 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2572 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2573 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2574 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2575 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2576 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2577 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2578 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2579 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2582 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2583 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2584 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2585 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2586 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2587 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2588 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2589 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2591 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2592 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2593 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2594 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2595 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2597 if (nr < 0 || nr >= MAX_PLAYERS)
2600 if (button_id == -1)
2603 sdl_js_button[nr][button_id] = button_state;
2606 void HandleJoystickEvent(Event *event)
2608 // when using joystick, disable overlay touch buttons
2609 runtime.uses_touch_device = FALSE;
2611 switch (event->type)
2613 case SDL_CONTROLLERDEVICEADDED:
2615 Debug("joystick", "SDL_CONTROLLERDEVICEADDED: device %d added",
2616 event->cdevice.which);
2621 case SDL_CONTROLLERDEVICEREMOVED:
2623 Debug("joystick", "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2624 event->cdevice.which);
2629 case SDL_CONTROLLERAXISMOTION:
2631 Debug("joystick", "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2632 event->caxis.which, event->caxis.axis, event->caxis.value);
2634 setJoystickAxis(event->caxis.which,
2636 event->caxis.value);
2639 case SDL_CONTROLLERBUTTONDOWN:
2641 Debug("joystick", "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2642 event->cbutton.which, event->cbutton.button);
2644 setJoystickButton(event->cbutton.which,
2645 event->cbutton.button,
2649 case SDL_CONTROLLERBUTTONUP:
2651 Debug("joystick", "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2652 event->cbutton.which, event->cbutton.button);
2654 setJoystickButton(event->cbutton.which,
2655 event->cbutton.button,
2659 case SDL_JOYAXISMOTION:
2660 if (sdl_is_controller[event->jaxis.which])
2664 Debug("joystick", "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2665 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2667 if (event->jaxis.axis < 4)
2668 setJoystickAxis(event->jaxis.which,
2670 event->jaxis.value);
2673 case SDL_JOYBUTTONDOWN:
2674 if (sdl_is_controller[event->jaxis.which])
2678 Debug("joystick", "SDL_JOYBUTTONDOWN: device %d, button %d",
2679 event->jbutton.which, event->jbutton.button);
2681 if (event->jbutton.button < 4)
2682 setJoystickButton(event->jbutton.which,
2683 event->jbutton.button,
2687 case SDL_JOYBUTTONUP:
2688 if (sdl_is_controller[event->jaxis.which])
2692 Debug("joystick", "SDL_JOYBUTTONUP: device %d, button %d",
2693 event->jbutton.which, event->jbutton.button);
2695 if (event->jbutton.button < 4)
2696 setJoystickButton(event->jbutton.which,
2697 event->jbutton.button,
2706 void SDLInitJoysticks(void)
2708 static boolean sdl_joystick_subsystem_initialized = FALSE;
2709 boolean print_warning = !sdl_joystick_subsystem_initialized;
2710 char *mappings_file_base = getPath2(options.conf_directory,
2711 GAMECONTROLLER_BASENAME);
2712 char *mappings_file_user = getPath2(getMainUserGameDataDir(),
2713 GAMECONTROLLER_BASENAME);
2717 if (!sdl_joystick_subsystem_initialized)
2719 sdl_joystick_subsystem_initialized = TRUE;
2721 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2723 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2724 Fail("SDL_Init() failed: %s", SDL_GetError());
2726 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2728 // the included game controller base mappings should always be found
2729 if (num_mappings == -1)
2730 Warn("no game controller base mappings found");
2733 Debug("joystick", "%d game controller base mapping(s) added",
2737 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2740 // the personal game controller user mappings may or may not be found
2741 if (num_mappings == -1)
2742 Warn("no game controller user mappings found");
2744 Debug("joystick", , "%d game controller user mapping(s) added",
2747 Debug("joystick", "%d joystick(s) found:", SDL_NumJoysticks());
2750 checked_free(mappings_file_base);
2751 checked_free(mappings_file_user);
2754 for (i = 0; i < SDL_NumJoysticks(); i++)
2756 const char *name, *type;
2758 if (SDL_IsGameController(i))
2760 name = SDL_GameControllerNameForIndex(i);
2761 type = "game controller";
2765 name = SDL_JoystickNameForIndex(i);
2769 Debug("joystick", "- joystick %d (%s): '%s'",
2770 i, type, (name ? name : "(Unknown)"));
2775 // assign joysticks from configured to connected joystick for all players
2776 for (i = 0; i < MAX_PLAYERS; i++)
2778 // get configured joystick for this player
2779 char *device_name = setup.input[i].joy.device_name;
2780 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2782 if (joystick_nr >= SDL_NumJoysticks())
2784 if (setup.input[i].use_joystick && print_warning)
2785 Warn("cannot find joystick %d", joystick_nr);
2790 // store configured joystick number for each player
2791 joystick.nr[i] = joystick_nr;
2794 // now open all connected joysticks (regardless if configured or not)
2795 for (i = 0; i < SDL_NumJoysticks(); i++)
2797 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2798 if (SDLCheckJoystickOpened(i))
2799 SDLCloseJoystick(i);
2801 if (SDLOpenJoystick(i))
2802 joystick.status = JOYSTICK_ACTIVATED;
2803 else if (print_warning)
2804 Warn("cannot open joystick %d", i);
2807 SDLClearJoystickState();
2810 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2812 if (nr < 0 || nr >= MAX_PLAYERS)
2816 *x = sdl_js_axis[nr][0];
2818 *y = sdl_js_axis[nr][1];
2821 *b1 = sdl_js_button[nr][0];
2823 *b2 = sdl_js_button[nr][1];
2829 // ============================================================================
2830 // touch input overlay functions
2831 // ============================================================================
2833 #if defined(USE_TOUCH_INPUT_OVERLAY)
2834 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2837 int grid_xsize = overlay.grid_xsize;
2838 int grid_ysize = overlay.grid_ysize;
2841 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2842 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2844 for (x = 0; x < grid_xsize; x++)
2846 rect.x = (x + 0) * video.screen_width / grid_xsize;
2847 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2849 for (y = 0; y < grid_ysize; y++)
2851 rect.y = (y + 0) * video.screen_height / grid_ysize;
2852 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2854 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2855 SDL_RenderDrawRect(sdl_renderer, &rect);
2859 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2862 static void RenderFillRectangle(int x, int y, int width, int height)
2864 SDL_Rect rect = { x, y, width, height };
2866 SDL_RenderFillRect(sdl_renderer, &rect);
2869 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2871 static int alpha_direction = 0;
2872 static int alpha_highlight = 0;
2873 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2874 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2876 int grid_xsize = overlay.grid_xsize;
2877 int grid_ysize = overlay.grid_ysize;
2880 if (alpha == alpha_max)
2882 if (alpha_direction < 0)
2884 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2886 if (alpha_highlight == 0)
2887 alpha_direction = 1;
2891 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2893 if (alpha_highlight == alpha_max)
2894 alpha_direction = -1;
2899 alpha_direction = 1;
2900 alpha_highlight = alpha;
2903 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2905 for (x = 0; x < grid_xsize; x++)
2907 for (y = 0; y < grid_ysize; y++)
2909 int grid_button = overlay.grid_button[x][y];
2910 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2911 int alpha_draw = alpha;
2912 int outline_border = MV_NONE;
2913 int border_size = 2;
2914 boolean draw_outlined = setup.touch.draw_outlined;
2915 boolean draw_pressed = setup.touch.draw_pressed;
2917 if (grid_button == CHAR_GRID_BUTTON_NONE)
2920 if (grid_button == overlay.grid_button_highlight)
2922 draw_outlined = FALSE;
2923 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2926 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2929 draw_outlined = FALSE;
2931 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2934 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2936 rect.x = (x + 0) * video.screen_width / grid_xsize;
2937 rect.y = (y + 0) * video.screen_height / grid_ysize;
2938 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2939 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2941 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2943 rect.x += border_size;
2944 rect.w -= border_size;
2946 outline_border |= MV_LEFT;
2949 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2951 rect.w -= border_size;
2953 outline_border |= MV_RIGHT;
2956 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2958 rect.y += border_size;
2959 rect.h -= border_size;
2961 outline_border |= MV_UP;
2964 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2966 rect.h -= border_size;
2968 outline_border |= MV_DOWN;
2973 int rect_x = rect.x +
2974 (outline_border & MV_LEFT ? border_size : 0);
2975 int rect_w = rect.w -
2976 (outline_border & MV_LEFT ? border_size : 0) -
2977 (outline_border & MV_RIGHT ? border_size : 0);
2979 if (outline_border & MV_LEFT)
2980 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2982 if (outline_border & MV_RIGHT)
2983 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2984 border_size, rect.h);
2986 if (outline_border & MV_UP)
2987 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2989 if (outline_border & MV_DOWN)
2990 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
2991 rect_w, border_size);
2995 SDL_RenderFillRect(sdl_renderer, &rect);
3000 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3003 static void DrawTouchInputOverlay(void)
3005 static boolean deactivated = TRUE;
3006 static boolean show_grid = FALSE;
3007 static int alpha = 0;
3008 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3009 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3010 boolean active = (overlay.enabled && overlay.active);
3012 if (!active && deactivated)
3017 if (alpha < alpha_max)
3018 alpha = MIN(alpha + alpha_step, alpha_max);
3020 deactivated = FALSE;
3024 alpha = MAX(0, alpha - alpha_step);
3030 if (overlay.show_grid)
3032 else if (deactivated)
3036 DrawTouchInputOverlay_ShowGrid(alpha);
3038 DrawTouchInputOverlay_ShowGridButtons(alpha);
3041 static void DrawTouchGadgetsOverlay(void)
3043 DrawGadgets_OverlayTouchButtons();