1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
19 #define ENABLE_UNUSED_CODE 0 // currently unused functions
21 #define DEBUG_JOYSTICKS 0
24 // ============================================================================
26 // ============================================================================
28 // SDL internal variables
29 static SDL_Window *sdl_window = NULL;
30 static SDL_Renderer *sdl_renderer = NULL;
31 static SDL_Texture *sdl_texture_stream = NULL;
32 static SDL_Texture *sdl_texture_target = NULL;
33 static boolean fullscreen_enabled = FALSE;
34 static boolean limit_screen_updates = FALSE;
37 // functions from SGE library
38 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
40 #if defined(USE_TOUCH_INPUT_OVERLAY)
41 // functions to draw overlay graphics for touch device input
42 static void DrawTouchInputOverlay(void);
43 static void DrawTouchGadgetsOverlay(void);
46 void SDLLimitScreenUpdates(boolean enable)
48 limit_screen_updates = enable;
51 static void FinalizeScreen(int draw_target)
53 // copy global animations to render target buffer, if defined (below border)
54 if (gfx.draw_global_anim_function != NULL)
55 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
57 // copy global masked border to render target buffer, if defined
58 if (gfx.draw_global_border_function != NULL)
59 gfx.draw_global_border_function(draw_target);
61 // copy global animations to render target buffer, if defined (above border)
62 if (gfx.draw_global_anim_function != NULL)
63 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
65 // copy tile selection cursor to render target buffer, if defined (above all)
66 if (gfx.draw_tile_cursor_function != NULL)
67 gfx.draw_tile_cursor_function(draw_target);
70 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
72 static DelayCounter update_screen_delay = { 50 }; // (milliseconds)
73 SDL_Surface *screen = backbuffer->surface;
75 if (limit_screen_updates &&
76 !DelayReached(&update_screen_delay))
79 LimitScreenUpdates(FALSE);
83 static int LastFrameCounter = 0;
84 boolean changed = (FrameCounter != LastFrameCounter);
86 Debug("internal:frame", "FrameCounter == %d [%s]", FrameCounter,
87 (changed ? "-" : "SAME FRAME UPDATED"));
89 LastFrameCounter = FrameCounter;
98 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
99 gfx.final_screen_bitmap != NULL) // may not be initialized yet
101 // draw global animations using bitmaps instead of using textures
102 // to prevent texture scaling artefacts (this is potentially slower)
104 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
105 gfx.win_xsize, gfx.win_ysize, 0, 0);
107 FinalizeScreen(DRAW_TO_SCREEN);
109 screen = gfx.final_screen_bitmap->surface;
111 // force full window redraw
115 SDL_Texture *sdl_texture = sdl_texture_stream;
117 // deactivate use of target texture if render targets are not supported
118 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
119 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
120 sdl_texture_target == NULL)
121 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
123 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
124 sdl_texture = sdl_texture_target;
128 int bytes_x = screen->pitch / video.width;
129 int bytes_y = screen->pitch;
131 SDL_UpdateTexture(sdl_texture, rect,
132 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
137 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
140 int xoff = video.screen_xoffset;
141 int yoff = video.screen_yoffset;
142 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
143 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
144 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
146 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
147 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
148 dst_rect2 = &dst_rect_screen;
150 dst_rect1 = &dst_rect_screen;
152 #if defined(HAS_SCREEN_KEYBOARD)
153 SDL_Rect src_rect_up = { 0, 0, video.width, video.height };
154 SDL_Rect dst_rect_up = dst_rect_screen;
156 if (video.shifted_up || video.shifted_up_delay.count)
158 int time_current = SDL_GetTicks();
159 int pos = video.shifted_up_pos;
160 int pos_last = video.shifted_up_pos_last;
162 if (!DelayReachedExt(&video.shifted_up_delay, time_current))
164 int delay_count = time_current - video.shifted_up_delay.count;
165 int delay_value = video.shifted_up_delay.value;
167 pos = pos_last + (pos - pos_last) * delay_count / delay_value;
171 video.shifted_up_pos_last = pos;
172 video.shifted_up_delay.count = 0;
176 src_rect_up.h = video.height - pos;
177 dst_rect_up.h = video.height - pos;
179 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
180 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
182 src_rect2 = &src_rect_up;
183 dst_rect2 = &dst_rect_up;
187 src_rect1 = &src_rect_up;
188 dst_rect1 = &dst_rect_up;
193 // clear render target buffer
194 SDL_RenderClear(sdl_renderer);
196 // set renderer to use target texture for rendering
197 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
198 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
199 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
201 // copy backbuffer texture to render target buffer
202 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
203 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
205 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
206 FinalizeScreen(DRAW_TO_SCREEN);
208 // when using target texture, copy it to screen buffer
209 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
210 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
212 SDL_SetRenderTarget(sdl_renderer, NULL);
213 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
216 #if defined(USE_TOUCH_INPUT_OVERLAY)
217 // draw overlay graphics for touch device input, if needed
218 DrawTouchInputOverlay();
220 // draw overlay gadgets for touch device input, if needed
221 DrawTouchGadgetsOverlay();
224 // global synchronization point of the game to align video frame delay
225 if (with_frame_delay)
226 WaitUntilDelayReached(&video.frame_delay);
228 video.frame_counter++;
230 // show render target buffer on screen
231 SDL_RenderPresent(sdl_renderer);
234 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
236 PumpEvents(); // execute event filter actions while waiting
238 UpdateScreenExt(rect, TRUE);
241 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
243 UpdateScreenExt(rect, FALSE);
246 void Delay_WithScreenUpdates(unsigned int delay)
248 unsigned int time_current = SDL_GetTicks();
249 unsigned int time_delayed = time_current + delay;
251 while (time_current < time_delayed)
253 // updating the screen contains waiting for frame delay (non-busy)
254 UpdateScreen_WithFrameDelay(NULL);
256 time_current = SDL_GetTicks();
260 static void SDLSetWindowIcon(char *basename)
262 // (setting the window icon on Mac OS X would replace the high-quality
263 // dock icon with the currently smaller (and uglier) icon from file)
265 #if !defined(PLATFORM_MAC)
266 char *filename = getCustomImageFilename(basename);
267 SDL_Surface *surface;
269 if (filename == NULL)
271 Warn("SDLSetWindowIcon(): cannot find file '%s'", basename);
276 if ((surface = IMG_Load(filename)) == NULL)
278 Warn("IMG_Load('%s') failed: %s", basename, SDL_GetError());
283 // set transparent color
284 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
285 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
287 SDL_SetWindowIcon(sdl_window, surface);
291 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
292 SDL_PixelFormat *format2)
294 return (format1->format == format2->format &&
295 format1->BitsPerPixel == format2->BitsPerPixel &&
296 format1->BytesPerPixel == format2->BytesPerPixel &&
297 format1->Rmask == format2->Rmask &&
298 format1->Gmask == format2->Gmask &&
299 format1->Bmask == format2->Bmask);
302 static void SDLCopyColorKey(SDL_Surface *src_surface, SDL_Surface *dst_surface)
307 // check if source surface has a color key
308 if (SDL_GetColorKey(src_surface, &color_key) == 0)
310 // get RGB values of color key of source surface
311 SDL_GetRGB(color_key, src_surface->format, &r, &g, &b);
313 // get color key from RGB values in destination surface format
314 color_key = SDL_MapRGB(dst_surface->format, r, g, b);
316 // set color key in destination surface
317 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL, color_key);
321 // unset color key in destination surface
322 SDL_SetColorKey(dst_surface, UNSET_TRANSPARENT_PIXEL, 0);
326 static boolean SDLHasColorKey(SDL_Surface *surface)
330 return (SDL_GetColorKey(surface, &color_key) == 0);
333 static boolean SDLHasAlpha(SDL_Surface *surface)
335 SDL_BlendMode blend_mode;
337 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
340 return (blend_mode == SDL_BLENDMODE_BLEND);
343 void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
345 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
347 SDL_SetSurfaceBlendMode(surface, blend_mode);
348 SDL_SetSurfaceAlphaMod(surface, alpha);
351 const char *SDLGetRendererName(void)
353 static SDL_RendererInfo renderer_info;
355 SDL_GetRendererInfo(sdl_renderer, &renderer_info);
357 return renderer_info.name;
360 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
362 SDL_PixelFormat format;
363 SDL_Surface *new_surface;
368 if (backbuffer && backbuffer->surface)
370 format = *backbuffer->surface->format;
371 format.Amask = surface->format->Amask; // keep alpha channel
375 format = *surface->format;
378 new_surface = SDL_ConvertSurface(surface, &format, 0);
380 if (new_surface == NULL)
381 Fail("SDL_ConvertSurface() failed: %s", SDL_GetError());
383 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
384 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
385 SDLCopyColorKey(surface, new_surface);
390 boolean SDLSetNativeSurface(SDL_Surface **surface)
392 SDL_Surface *new_surface;
394 if (surface == NULL ||
396 backbuffer == NULL ||
397 backbuffer->surface == NULL)
400 // if pixel format already optimized for destination surface, do nothing
401 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
404 new_surface = SDLGetNativeSurface(*surface);
406 SDL_FreeSurface(*surface);
408 *surface = new_surface;
413 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
415 if (program.headless)
418 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
421 Fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
426 void SDLCreateBitmapTextures(Bitmap *bitmap)
432 SDL_DestroyTexture(bitmap->texture);
433 if (bitmap->texture_masked)
434 SDL_DestroyTexture(bitmap->texture_masked);
436 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
437 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
440 void SDLFreeBitmapTextures(Bitmap *bitmap)
446 SDL_DestroyTexture(bitmap->texture);
447 if (bitmap->texture_masked)
448 SDL_DestroyTexture(bitmap->texture_masked);
450 bitmap->texture = NULL;
451 bitmap->texture_masked = NULL;
454 void SDLInitVideoDisplay(void)
456 // set hint to select render driver as specified in setup config file
457 if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
458 SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
460 // initialize SDL video
461 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
462 Fail("SDL_InitSubSystem() failed: %s", SDL_GetError());
464 // set default SDL depth
465 video.default_depth = 32; // (how to determine video depth in SDL2?)
467 // Code used with SDL 1.2:
468 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
471 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
473 if (program.headless)
476 video.window_scaling_percent = setup.window_scaling_percent;
477 video.window_scaling_quality = setup.window_scaling_quality;
479 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
481 // SDL 2.0: support for (desktop) fullscreen mode available
482 video.fullscreen_available = TRUE;
484 // open SDL video output device (window or fullscreen mode)
485 if (!SDLSetVideoMode(fullscreen))
486 Fail("setting video mode failed");
488 // !!! SDL2 can only set the window icon if the window already exists !!!
490 SDLSetWindowIcon(program.icon_filename);
492 // set window and icon title
496 static void SDLInitVideoBuffer_DrawBuffer(void)
498 /* SDL cannot directly draw to the visible video framebuffer like X11,
499 but always uses a backbuffer, which is then blitted to the visible
500 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
501 visible video framebuffer with 'SDL_Flip', if the hardware supports
502 this). Therefore do not use an additional backbuffer for drawing, but
503 use a symbolic buffer (distinguishable from the SDL backbuffer) called
504 'window', which indicates that the SDL backbuffer should be updated to
505 the visible video framebuffer when attempting to blit to it.
507 For convenience, it seems to be a good idea to create this symbolic
508 buffer 'window' at the same size as the SDL backbuffer. Although it
509 should never be drawn to directly, it would do no harm nevertheless. */
511 // create additional (symbolic) buffer for double-buffering
512 ReCreateBitmap(&window, video.width, video.height);
514 // create dummy drawing buffer for headless mode, if needed
515 if (program.headless)
516 ReCreateBitmap(&backbuffer, video.width, video.height);
519 void SDLInitVideoBuffer(boolean fullscreen)
521 SDLInitVideoBuffer_VideoBuffer(fullscreen);
522 SDLInitVideoBuffer_DrawBuffer();
525 static boolean SDLCreateScreen(boolean fullscreen)
527 SDL_Surface *new_surface = NULL;
529 int surface_flags_window = SURFACE_FLAGS;
530 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
533 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
535 video.vsync_mode = VSYNC_MODE_OFF;
537 if (!strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF))
539 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
540 video.vsync_mode = VSYNC_MODE_NORMAL;
543 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
544 _without_ enabling 2D/3D acceleration and/or guest additions installed,
545 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
546 it will try to use accelerated graphics and apparently fails miserably) */
547 int renderer_flags = SDL_RENDERER_SOFTWARE;
550 int width = video.width;
551 int height = video.height;
552 int screen_width = video.screen_width;
553 int screen_height = video.screen_height;
554 int surface_flags = (fullscreen ? surface_flags_fullscreen :
555 surface_flags_window);
556 int display_nr = options.display_nr;
558 // default window size is unscaled
559 video.window_width = screen_width;
560 video.window_height = screen_height;
562 // store if initial screen mode is fullscreen mode when changing screen size
563 video.fullscreen_initial = fullscreen;
565 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
567 video.window_width = window_scaling_factor * screen_width;
568 video.window_height = window_scaling_factor * screen_height;
570 if (sdl_texture_stream)
572 SDL_DestroyTexture(sdl_texture_stream);
573 sdl_texture_stream = NULL;
576 if (sdl_texture_target)
578 SDL_DestroyTexture(sdl_texture_target);
579 sdl_texture_target = NULL;
582 if (!(fullscreen && fullscreen_enabled))
586 SDL_DestroyRenderer(sdl_renderer);
592 SDL_SetWindowSize(sdl_window, video.window_width, video.window_height);
596 if (sdl_window == NULL)
597 sdl_window = SDL_CreateWindow(program.window_title,
598 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
599 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
604 if (sdl_window != NULL)
606 if (sdl_renderer == NULL)
607 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
609 if (sdl_renderer != NULL)
611 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
612 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
614 // required for setting adaptive vsync when using OpenGL renderer
615 SDLSetScreenVsyncMode(setup.vsync_mode);
617 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
618 SDL_PIXELFORMAT_ARGB8888,
619 SDL_TEXTUREACCESS_STREAMING,
622 if (SDL_RenderTargetSupported(sdl_renderer))
623 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
624 SDL_PIXELFORMAT_ARGB8888,
625 SDL_TEXTUREACCESS_TARGET,
628 if (sdl_texture_stream != NULL)
630 // use SDL default values for RGB masks and no alpha channel
631 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
633 if (new_surface == NULL)
634 Warn("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
638 Warn("SDL_CreateTexture() failed: %s", SDL_GetError());
643 Warn("SDL_CreateRenderer() failed: %s", SDL_GetError());
648 Warn("SDL_CreateWindow() failed: %s", SDL_GetError());
651 SDLSetScreenProperties();
653 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
654 if (new_surface != NULL)
655 fullscreen_enabled = fullscreen;
657 if (backbuffer == NULL)
658 backbuffer = CreateBitmapStruct();
660 backbuffer->width = video.width;
661 backbuffer->height = video.height;
663 if (backbuffer->surface)
664 SDL_FreeSurface(backbuffer->surface);
666 backbuffer->surface = new_surface;
668 return (new_surface != NULL);
671 boolean SDLSetVideoMode(boolean fullscreen)
673 boolean success = FALSE;
677 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
679 // switch display to fullscreen mode, if available
680 success = SDLCreateScreen(TRUE);
684 // switching display to fullscreen mode failed -- do not try it again
685 video.fullscreen_available = FALSE;
689 video.fullscreen_enabled = TRUE;
693 if ((!fullscreen && video.fullscreen_enabled) || !success)
695 // switch display to window mode
696 success = SDLCreateScreen(FALSE);
700 // switching display to window mode failed -- should not happen
704 video.fullscreen_enabled = FALSE;
705 video.window_scaling_percent = setup.window_scaling_percent;
706 video.window_scaling_quality = setup.window_scaling_quality;
708 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
712 SDLRedrawWindow(); // map window
717 void SDLSetWindowTitle(void)
719 if (sdl_window == NULL)
722 SDL_SetWindowTitle(sdl_window, program.window_title);
725 void SDLSetWindowScaling(int window_scaling_percent)
727 if (sdl_window == NULL)
730 float window_scaling_factor = (float)window_scaling_percent / 100;
731 int new_window_width = (int)(window_scaling_factor * video.screen_width);
732 int new_window_height = (int)(window_scaling_factor * video.screen_height);
734 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
736 video.window_scaling_percent = window_scaling_percent;
737 video.window_width = new_window_width;
738 video.window_height = new_window_height;
743 void SDLSetWindowScalingQuality(char *window_scaling_quality)
745 SDL_Texture *new_texture;
747 if (sdl_texture_stream == NULL)
750 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
752 new_texture = SDL_CreateTexture(sdl_renderer,
753 SDL_PIXELFORMAT_ARGB8888,
754 SDL_TEXTUREACCESS_STREAMING,
755 video.width, video.height);
757 if (new_texture != NULL)
759 SDL_DestroyTexture(sdl_texture_stream);
761 sdl_texture_stream = new_texture;
764 if (SDL_RenderTargetSupported(sdl_renderer))
765 new_texture = SDL_CreateTexture(sdl_renderer,
766 SDL_PIXELFORMAT_ARGB8888,
767 SDL_TEXTUREACCESS_TARGET,
768 video.width, video.height);
772 if (new_texture != NULL)
774 SDL_DestroyTexture(sdl_texture_target);
776 sdl_texture_target = new_texture;
781 video.window_scaling_quality = window_scaling_quality;
784 void SDLSetWindowFullscreen(boolean fullscreen)
786 if (sdl_window == NULL)
789 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
791 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
792 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
794 // if screen size was changed in fullscreen mode, correct desktop window size
795 if (!fullscreen && video.fullscreen_initial)
797 SDLSetWindowScaling(setup.window_scaling_percent);
798 SDL_SetWindowPosition(sdl_window,
799 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr),
800 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr));
802 video.fullscreen_initial = FALSE;
806 void SDLSetDisplaySize(void)
808 if (sdl_renderer != NULL)
812 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
814 video.display_width = w;
815 video.display_height = h;
818 Debug("video", "SDL renderer size: %d x %d",
819 video.display_width, video.display_height);
824 SDL_Rect display_bounds;
826 SDL_GetDisplayBounds(0, &display_bounds);
828 video.display_width = display_bounds.w;
829 video.display_height = display_bounds.h;
832 Debug("video", "SDL display size: %d x %d",
833 video.display_width, video.display_height);
838 void SDLSetScreenSizeAndOffsets(int width, int height)
840 // set default video screen size and offsets
841 video.screen_width = width;
842 video.screen_height = height;
843 video.screen_xoffset = 0;
844 video.screen_yoffset = 0;
846 #if defined(USE_COMPLETE_DISPLAY)
847 float ratio_video = (float) width / height;
848 float ratio_display = (float) video.display_width / video.display_height;
850 if (ratio_video != ratio_display)
852 // adjust drawable screen size to cover the whole device display
854 if (ratio_video < ratio_display)
855 video.screen_width *= ratio_display / ratio_video;
857 video.screen_height *= ratio_video / ratio_display;
859 video.screen_xoffset = (video.screen_width - width) / 2;
860 video.screen_yoffset = (video.screen_height - height) / 2;
863 Debug("video", "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
865 video.screen_width, video.screen_height,
866 ratio_video, ratio_display);
872 void SDLSetScreenSizeForRenderer(int width, int height)
874 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
877 void SDLSetScreenProperties(void)
880 SDLSetScreenSizeAndOffsets(video.width, video.height);
881 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
883 SetOverlayGridSizeAndButtons();
886 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
888 video.screen_rendering_mode =
889 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
890 SPECIAL_RENDERING_BITMAP :
891 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
892 SPECIAL_RENDERING_TARGET:
893 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
894 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
897 void SDLSetScreenVsyncMode(char *vsync_mode)
899 // changing vsync mode without re-creating renderer only supported by OpenGL
900 if (!strPrefixLower((char *)SDLGetRendererName(), "opengl"))
903 int interval = VSYNC_MODE_STR_TO_INT(vsync_mode);
904 int result = SDL_GL_SetSwapInterval(interval);
906 // if adaptive vsync requested, but not supported, retry with normal vsync
907 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
909 interval = VSYNC_MODE_NORMAL;
911 result = SDL_GL_SetSwapInterval(interval);
915 interval = VSYNC_MODE_OFF;
917 video.vsync_mode = interval;
920 void SDLRedrawWindow(void)
922 UpdateScreen_WithoutFrameDelay(NULL);
925 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
928 if (program.headless)
931 SDL_Surface *surface =
932 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
935 Fail("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
937 SDLSetNativeSurface(&surface);
939 bitmap->surface = surface;
942 void SDLFreeBitmapPointers(Bitmap *bitmap)
945 SDL_FreeSurface(bitmap->surface);
946 if (bitmap->surface_masked)
947 SDL_FreeSurface(bitmap->surface_masked);
949 bitmap->surface = NULL;
950 bitmap->surface_masked = NULL;
953 SDL_DestroyTexture(bitmap->texture);
954 if (bitmap->texture_masked)
955 SDL_DestroyTexture(bitmap->texture_masked);
957 bitmap->texture = NULL;
958 bitmap->texture_masked = NULL;
961 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
962 int src_x, int src_y, int width, int height,
963 int dst_x, int dst_y, int mask_mode)
965 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
966 SDL_Rect src_rect, dst_rect;
978 // if (src_bitmap != backbuffer || dst_bitmap != window)
979 if (!(src_bitmap == backbuffer && dst_bitmap == window))
980 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
981 src_bitmap->surface_masked : src_bitmap->surface),
982 &src_rect, real_dst_bitmap->surface, &dst_rect);
984 if (dst_bitmap == window)
985 UpdateScreen_WithFrameDelay(&dst_rect);
988 void SDLBlitTexture(Bitmap *bitmap,
989 int src_x, int src_y, int width, int height,
990 int dst_x, int dst_y, int mask_mode)
992 SDL_Texture *texture;
997 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1005 src_rect.h = height;
1010 dst_rect.h = height;
1012 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1015 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1018 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1026 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1028 if (dst_bitmap == window)
1029 UpdateScreen_WithFrameDelay(&rect);
1032 void PrepareFadeBitmap(int draw_target)
1034 Bitmap *fade_bitmap =
1035 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1036 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1038 if (fade_bitmap == NULL)
1041 // copy backbuffer to fading buffer
1042 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1044 // add border and animations to fading buffer
1045 FinalizeScreen(draw_target);
1048 void SDLFadeRectangle(int x, int y, int width, int height,
1049 int fade_mode, int fade_delay, int post_delay,
1050 void (*draw_border_function)(void))
1052 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1053 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1054 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1055 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1056 SDL_Surface *surface_screen = backbuffer->surface;
1057 SDL_Rect src_rect, dst_rect;
1059 int src_x = x, src_y = y;
1060 int dst_x = x, dst_y = y;
1061 unsigned int time_last, time_current;
1063 // store function for drawing global masked border
1064 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1066 // deactivate drawing of global border while fading, if needed
1067 if (draw_border_function == NULL)
1068 gfx.draw_global_border_function = NULL;
1073 src_rect.h = height;
1077 dst_rect.w = width; // (ignored)
1078 dst_rect.h = height; // (ignored)
1080 dst_rect2 = dst_rect;
1082 // before fading in, store backbuffer (without animation graphics)
1083 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1084 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1086 // copy source and target surfaces to temporary surfaces for fading
1087 if (fade_mode & FADE_TYPE_TRANSFORM)
1089 // (source and target fading buffer already prepared)
1091 else if (fade_mode & FADE_TYPE_FADE_IN)
1093 // (target fading buffer already prepared)
1094 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1096 else // FADE_TYPE_FADE_OUT
1098 // (source fading buffer already prepared)
1099 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1102 time_current = SDL_GetTicks();
1104 if (fade_delay <= 0)
1106 // immediately draw final target frame without delay
1107 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1111 // when fading without delay, also skip post delay
1115 if (fade_mode == FADE_MODE_MELT)
1117 boolean done = FALSE;
1118 int melt_pixels = 2;
1119 int melt_columns = width / melt_pixels;
1120 int ypos[melt_columns];
1121 int max_steps = height / 8 + 32;
1126 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1128 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1130 ypos[0] = -GetSimpleRandom(16);
1132 for (i = 1 ; i < melt_columns; i++)
1134 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1136 ypos[i] = ypos[i - 1] + r;
1149 time_last = time_current;
1150 time_current = SDL_GetTicks();
1151 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1152 steps_final = MIN(MAX(0, steps), max_steps);
1156 done = (steps_done >= steps_final);
1158 for (i = 0 ; i < melt_columns; i++)
1166 else if (ypos[i] < height)
1171 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1173 if (ypos[i] + dy >= height)
1174 dy = height - ypos[i];
1176 // copy part of (appearing) target surface to upper area
1177 src_rect.x = src_x + i * melt_pixels;
1178 // src_rect.y = src_y + ypos[i];
1180 src_rect.w = melt_pixels;
1182 src_rect.h = ypos[i] + dy;
1184 dst_rect.x = dst_x + i * melt_pixels;
1185 // dst_rect.y = dst_y + ypos[i];
1188 if (steps_done >= steps_final)
1189 SDL_BlitSurface(surface_target, &src_rect,
1190 surface_screen, &dst_rect);
1194 // copy part of (disappearing) source surface to lower area
1195 src_rect.x = src_x + i * melt_pixels;
1197 src_rect.w = melt_pixels;
1198 src_rect.h = height - ypos[i];
1200 dst_rect.x = dst_x + i * melt_pixels;
1201 dst_rect.y = dst_y + ypos[i];
1203 if (steps_done >= steps_final)
1204 SDL_BlitSurface(surface_source, &src_rect,
1205 surface_screen, &dst_rect);
1211 src_rect.x = src_x + i * melt_pixels;
1213 src_rect.w = melt_pixels;
1214 src_rect.h = height;
1216 dst_rect.x = dst_x + i * melt_pixels;
1219 if (steps_done >= steps_final)
1220 SDL_BlitSurface(surface_target, &src_rect,
1221 surface_screen, &dst_rect);
1225 if (steps_done >= steps_final)
1227 if (draw_border_function != NULL)
1228 draw_border_function();
1230 UpdateScreen_WithFrameDelay(&dst_rect2);
1232 if (PendingEscapeKeyEvent())
1237 else if (fade_mode == FADE_MODE_CURTAIN)
1241 int xx_size = width / 2;
1243 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1245 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1247 for (xx = 0; xx < xx_size;)
1249 time_last = time_current;
1250 time_current = SDL_GetTicks();
1251 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1252 xx_final = MIN(MAX(0, xx), xx_size);
1257 src_rect.h = height;
1262 // draw new (target) image to screen buffer
1263 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1265 if (xx_final < xx_size)
1267 src_rect.w = xx_size - xx_final;
1268 src_rect.h = height;
1270 // draw old (source) image to screen buffer (left side)
1272 src_rect.x = src_x + xx_final;
1275 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1277 // draw old (source) image to screen buffer (right side)
1279 src_rect.x = src_x + xx_size;
1280 dst_rect.x = dst_x + xx_size + xx_final;
1282 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1285 if (draw_border_function != NULL)
1286 draw_border_function();
1288 // only update the region of the screen that is affected from fading
1289 UpdateScreen_WithFrameDelay(&dst_rect2);
1291 if (PendingEscapeKeyEvent())
1295 else // fading in, fading out or cross-fading
1300 for (alpha = 0.0; alpha < 255.0;)
1302 time_last = time_current;
1303 time_current = SDL_GetTicks();
1304 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1305 alpha_final = MIN(MAX(0, alpha), 255);
1307 // draw existing (source) image to screen buffer
1308 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1310 // draw new (target) image to screen buffer using alpha blending
1311 SDLSetAlpha(surface_target, TRUE, alpha_final);
1312 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1314 if (draw_border_function != NULL)
1315 draw_border_function();
1317 // only update the region of the screen that is affected from fading
1318 UpdateScreen_WithFrameDelay(&dst_rect);
1320 if (PendingEscapeKeyEvent())
1326 Delay_WithScreenUpdates(post_delay);
1328 // restore function for drawing global masked border
1329 gfx.draw_global_border_function = draw_global_border_function;
1331 // after fading in, restore backbuffer (without animation graphics)
1332 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1333 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1336 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1337 int to_x, int to_y, Uint32 color)
1339 SDL_Surface *surface = dst_bitmap->surface;
1343 swap_numbers(&from_x, &to_x);
1346 swap_numbers(&from_y, &to_y);
1350 rect.w = (to_x - from_x + 1);
1351 rect.h = (to_y - from_y + 1);
1353 SDL_FillRect(surface, &rect, color);
1356 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1357 int to_x, int to_y, Uint32 color)
1359 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1362 #if ENABLE_UNUSED_CODE
1363 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1364 int num_points, Uint32 color)
1369 for (i = 0; i < num_points - 1; i++)
1371 for (x = 0; x < line_width; x++)
1373 for (y = 0; y < line_width; y++)
1375 int dx = x - line_width / 2;
1376 int dy = y - line_width / 2;
1378 if ((x == 0 && y == 0) ||
1379 (x == 0 && y == line_width - 1) ||
1380 (x == line_width - 1 && y == 0) ||
1381 (x == line_width - 1 && y == line_width - 1))
1384 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1385 points[i+1].x + dx, points[i+1].y + dy, color);
1392 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1394 SDL_Surface *surface = src_bitmap->surface;
1396 switch (surface->format->BytesPerPixel)
1398 case 1: // assuming 8-bpp
1400 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1404 case 2: // probably 15-bpp or 16-bpp
1406 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1410 case 3: // slow 24-bpp mode; usually not used
1413 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1417 shift = surface->format->Rshift;
1418 color |= *(pix + shift / 8) >> shift;
1419 shift = surface->format->Gshift;
1420 color |= *(pix + shift / 8) >> shift;
1421 shift = surface->format->Bshift;
1422 color |= *(pix + shift / 8) >> shift;
1428 case 4: // probably 32-bpp
1430 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1439 // ============================================================================
1440 // The following functions were taken from the SGE library
1441 // (SDL Graphics Extension Library) by Anders Lindström
1442 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1443 // ============================================================================
1445 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1447 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1449 switch (surface->format->BytesPerPixel)
1454 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1460 // Probably 15-bpp or 16-bpp
1461 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1467 // Slow 24-bpp mode, usually not used
1471 // Gack - slow, but endian correct
1472 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1473 shift = surface->format->Rshift;
1474 *(pix+shift/8) = color>>shift;
1475 shift = surface->format->Gshift;
1476 *(pix+shift/8) = color>>shift;
1477 shift = surface->format->Bshift;
1478 *(pix+shift/8) = color>>shift;
1485 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1493 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1494 Uint8 R, Uint8 G, Uint8 B)
1496 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1499 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1501 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1504 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1506 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1509 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1514 // Gack - slow, but endian correct
1515 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1516 shift = surface->format->Rshift;
1517 *(pix+shift/8) = color>>shift;
1518 shift = surface->format->Gshift;
1519 *(pix+shift/8) = color>>shift;
1520 shift = surface->format->Bshift;
1521 *(pix+shift/8) = color>>shift;
1524 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1526 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1529 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1531 switch (dest->format->BytesPerPixel)
1534 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1538 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1542 _PutPixel24(dest,x,y,color);
1546 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1552 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1554 if (SDL_MUSTLOCK(surface))
1556 if (SDL_LockSurface(surface) < 0)
1562 _PutPixel(surface, x, y, color);
1564 if (SDL_MUSTLOCK(surface))
1566 SDL_UnlockSurface(surface);
1571 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1572 Uint8 r, Uint8 g, Uint8 b)
1574 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1577 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1579 if (y >= 0 && y <= dest->h - 1)
1581 switch (dest->format->BytesPerPixel)
1584 return y*dest->pitch;
1588 return y*dest->pitch/2;
1592 return y*dest->pitch;
1596 return y*dest->pitch/4;
1604 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1607 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1609 switch (surface->format->BytesPerPixel)
1614 *((Uint8 *)surface->pixels + ypitch + x) = color;
1620 // Probably 15-bpp or 16-bpp
1621 *((Uint16 *)surface->pixels + ypitch + x) = color;
1627 // Slow 24-bpp mode, usually not used
1631 // Gack - slow, but endian correct
1632 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1633 shift = surface->format->Rshift;
1634 *(pix+shift/8) = color>>shift;
1635 shift = surface->format->Gshift;
1636 *(pix+shift/8) = color>>shift;
1637 shift = surface->format->Bshift;
1638 *(pix+shift/8) = color>>shift;
1645 *((Uint32 *)surface->pixels + ypitch + x) = color;
1652 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1657 if (SDL_MUSTLOCK(Surface))
1659 if (SDL_LockSurface(Surface) < 0)
1673 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1677 if (x2 > Surface->w - 1)
1678 x2 = Surface->w - 1;
1685 SDL_FillRect(Surface, &l, Color);
1687 if (SDL_MUSTLOCK(Surface))
1689 SDL_UnlockSurface(Surface);
1693 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1694 Uint8 R, Uint8 G, Uint8 B)
1696 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1699 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1712 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1716 if (x2 > Surface->w - 1)
1717 x2 = Surface->w - 1;
1724 SDL_FillRect(Surface, &l, Color);
1727 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1732 if (SDL_MUSTLOCK(Surface))
1734 if (SDL_LockSurface(Surface) < 0)
1748 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1752 if (y2 > Surface->h - 1)
1753 y2 = Surface->h - 1;
1760 SDL_FillRect(Surface, &l, Color);
1762 if (SDL_MUSTLOCK(Surface))
1764 SDL_UnlockSurface(Surface);
1768 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1769 Uint8 R, Uint8 G, Uint8 B)
1771 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1774 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1787 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1791 if (y2 > Surface->h - 1)
1792 y2 = Surface->h - 1;
1799 SDL_FillRect(Surface, &l, Color);
1803 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1804 Sint16 x2, Sint16 y2, Uint32 Color,
1805 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1808 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1813 sdx = (dx < 0) ? -1 : 1;
1814 sdy = (dy < 0) ? -1 : 1;
1826 for (x = 0; x < dx; x++)
1828 Callback(Surface, px, py, Color);
1842 for (y = 0; y < dy; y++)
1844 Callback(Surface, px, py, Color);
1859 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1860 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1861 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1864 sge_DoLine(Surface, X1, Y1, X2, Y2,
1865 SDL_MapRGB(Surface->format, R, G, B), Callback);
1869 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1872 if (SDL_MUSTLOCK(Surface))
1874 if (SDL_LockSurface(Surface) < 0)
1879 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1881 // unlock the display
1882 if (SDL_MUSTLOCK(Surface))
1884 SDL_UnlockSurface(Surface);
1889 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1890 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1892 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1896 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1898 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1902 // ----------------------------------------------------------------------------
1903 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1904 // ----------------------------------------------------------------------------
1906 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1907 int width, int height, Uint32 color)
1911 for (y = src_y; y < src_y + height; y++)
1913 for (x = src_x; x < src_x + width; x++)
1915 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1917 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1922 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1923 int src_x, int src_y, int width, int height,
1924 int dst_x, int dst_y)
1928 for (y = 0; y < height; y++)
1930 for (x = 0; x < width; x++)
1932 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1934 if (pixel != BLACK_PIXEL)
1935 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1941 // ============================================================================
1942 // The following functions were taken from the SDL_gfx library version 2.0.3
1943 // (Rotozoomer) by Andreas Schiffler
1944 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1945 // ============================================================================
1947 // ----------------------------------------------------------------------------
1950 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1951 // ----------------------------------------------------------------------------
1961 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1964 tColorRGBA *sp, *csp, *dp;
1968 sp = csp = (tColorRGBA *) src->pixels;
1969 dp = (tColorRGBA *) dst->pixels;
1970 dgap = dst->pitch - dst->w * 4;
1972 for (y = 0; y < dst->h; y++)
1976 for (x = 0; x < dst->w; x++)
1978 tColorRGBA *sp0 = sp;
1979 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1980 tColorRGBA *sp00 = &sp0[0];
1981 tColorRGBA *sp01 = &sp0[1];
1982 tColorRGBA *sp10 = &sp1[0];
1983 tColorRGBA *sp11 = &sp1[1];
1986 // create new color pixel from all four source color pixels
1987 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1988 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1989 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1990 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1995 // advance source pointers
1998 // advance destination pointer
2002 // advance source pointer
2003 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2005 // advance destination pointers
2006 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2012 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2014 int x, y, *sax, *say, *csax, *csay;
2016 tColorRGBA *sp, *csp, *csp0, *dp;
2019 // use specialized zoom function when scaling down to exactly half size
2020 if (src->w == 2 * dst->w &&
2021 src->h == 2 * dst->h)
2022 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2025 sx = (float) src->w / (float) dst->w;
2026 sy = (float) src->h / (float) dst->h;
2028 // allocate memory for row increments
2029 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2030 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2032 // precalculate row increments
2033 for (x = 0; x <= dst->w; x++)
2034 *csax++ = (int)(sx * x);
2036 for (y = 0; y <= dst->h; y++)
2037 *csay++ = (int)(sy * y);
2040 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2041 dp = (tColorRGBA *) dst->pixels;
2042 dgap = dst->pitch - dst->w * 4;
2045 for (y = 0; y < dst->h; y++)
2050 for (x = 0; x < dst->w; x++)
2055 // advance source pointers
2059 // advance destination pointer
2063 // advance source pointer
2065 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2067 // advance destination pointers
2068 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2077 // ----------------------------------------------------------------------------
2080 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2081 // ----------------------------------------------------------------------------
2083 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2085 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2086 Uint8 *sp, *dp, *csp;
2090 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2091 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2093 // allocate memory for row increments
2094 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2095 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2097 // precalculate row increments
2100 for (x = 0; x < dst->w; x++)
2103 *csax = (csx >> 16);
2110 for (y = 0; y < dst->h; y++)
2113 *csay = (csy >> 16);
2120 for (x = 0; x < dst->w; x++)
2128 for (y = 0; y < dst->h; y++)
2135 sp = csp = (Uint8 *) src->pixels;
2136 dp = (Uint8 *) dst->pixels;
2137 dgap = dst->pitch - dst->w;
2141 for (y = 0; y < dst->h; y++)
2145 for (x = 0; x < dst->w; x++)
2150 // advance source pointers
2154 // advance destination pointer
2158 // advance source pointer (for row)
2159 csp += ((*csay) * src->pitch);
2162 // advance destination pointers
2172 // ----------------------------------------------------------------------------
2175 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2176 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2177 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2178 // into a 32bit RGBA format on the fly.
2179 // ----------------------------------------------------------------------------
2181 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2183 SDL_Surface *zoom_src = NULL;
2184 SDL_Surface *zoom_dst = NULL;
2185 boolean is_converted = FALSE;
2192 // determine if source surface is 32 bit or 8 bit
2193 is_32bit = (src->format->BitsPerPixel == 32);
2195 if (is_32bit || src->format->BitsPerPixel == 8)
2197 // use source surface 'as is'
2202 // new source surface is 32 bit with a defined RGB ordering
2203 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2204 0x000000ff, 0x0000ff00, 0x00ff0000,
2205 (src->format->Amask ? 0xff000000 : 0));
2206 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2208 is_converted = TRUE;
2211 // allocate surface to completely contain the zoomed surface
2214 // target surface is 32 bit with source RGBA/ABGR ordering
2215 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2216 zoom_src->format->Rmask,
2217 zoom_src->format->Gmask,
2218 zoom_src->format->Bmask,
2219 zoom_src->format->Amask);
2223 // target surface is 8 bit
2224 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2228 // lock source surface
2229 SDL_LockSurface(zoom_src);
2231 // check which kind of surface we have
2234 // call the 32 bit transformation routine to do the zooming
2235 zoomSurfaceRGBA(zoom_src, zoom_dst);
2240 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2241 zoom_dst->format->palette->colors[i] =
2242 zoom_src->format->palette->colors[i];
2243 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2245 // call the 8 bit transformation routine to do the zooming
2246 zoomSurfaceY(zoom_src, zoom_dst);
2249 // unlock source surface
2250 SDL_UnlockSurface(zoom_src);
2252 // free temporary surface
2254 SDL_FreeSurface(zoom_src);
2256 // return destination surface
2260 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2262 SDL_Surface *new_surface;
2264 if (surface == NULL)
2267 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2268 Fail("SDLGetNativeSurface() failed");
2270 // remove alpha channel from native non-transparent surface, if defined
2271 SDLSetAlpha(new_surface, FALSE, 0);
2273 // remove transparent color from native non-transparent surface, if defined
2274 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2279 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2281 Bitmap *dst_bitmap = CreateBitmapStruct();
2282 SDL_Surface *src_surface = src_bitmap->surface_masked;
2283 SDL_Surface *dst_surface;
2285 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2286 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2288 dst_bitmap->width = dst_width;
2289 dst_bitmap->height = dst_height;
2291 // create zoomed temporary surface from source surface
2292 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2294 // create native format destination surface from zoomed temporary surface
2295 SDLSetNativeSurface(&dst_surface);
2297 // set color key for zoomed surface from source surface, if defined
2298 if (SDLHasColorKey(src_surface))
2299 SDLCopyColorKey(src_surface, dst_surface);
2301 // create native non-transparent surface for opaque blitting
2302 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2304 // set native transparent surface for masked blitting
2305 dst_bitmap->surface_masked = dst_surface;
2311 // ============================================================================
2312 // load image to bitmap
2313 // ============================================================================
2315 Bitmap *SDLLoadImage(char *filename)
2317 Bitmap *new_bitmap = CreateBitmapStruct();
2318 SDL_Surface *sdl_image_tmp;
2320 if (program.headless)
2322 // prevent sanity check warnings at later stage
2323 new_bitmap->width = new_bitmap->height = 1;
2328 print_timestamp_init("SDLLoadImage");
2330 print_timestamp_time(getBaseNamePtr(filename));
2332 // load image to temporary surface
2333 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2334 Fail("IMG_Load('%s') failed: %s", getBaseNamePtr(filename), SDL_GetError());
2336 print_timestamp_time("IMG_Load");
2338 UPDATE_BUSY_STATE();
2340 // create native non-transparent surface for current image
2341 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2342 Fail("SDLGetOpaqueSurface() failed");
2344 print_timestamp_time("SDLGetNativeSurface (opaque)");
2346 UPDATE_BUSY_STATE();
2348 // set black pixel to transparent if no alpha channel / transparent color
2349 if (!SDLHasAlpha(sdl_image_tmp) &&
2350 !SDLHasColorKey(sdl_image_tmp))
2351 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2352 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2354 // create native transparent surface for current image
2355 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2356 Fail("SDLGetNativeSurface() failed");
2358 print_timestamp_time("SDLGetNativeSurface (masked)");
2360 UPDATE_BUSY_STATE();
2362 // free temporary surface
2363 SDL_FreeSurface(sdl_image_tmp);
2365 new_bitmap->width = new_bitmap->surface->w;
2366 new_bitmap->height = new_bitmap->surface->h;
2368 print_timestamp_done("SDLLoadImage");
2374 // ----------------------------------------------------------------------------
2375 // custom cursor fuctions
2376 // ----------------------------------------------------------------------------
2378 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2380 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2381 cursor_info->width, cursor_info->height,
2382 cursor_info->hot_x, cursor_info->hot_y);
2385 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2387 static struct MouseCursorInfo *last_cursor_info = NULL;
2388 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2389 static SDL_Cursor *cursor_default = NULL;
2390 static SDL_Cursor *cursor_current = NULL;
2392 // if invoked for the first time, store the SDL default cursor
2393 if (cursor_default == NULL)
2394 cursor_default = SDL_GetCursor();
2396 // only create new cursor if cursor info (custom only) has changed
2397 if (cursor_info != NULL && cursor_info != last_cursor_info)
2399 cursor_current = create_cursor(cursor_info);
2400 last_cursor_info = cursor_info;
2403 // only set new cursor if cursor info (custom or NULL) has changed
2404 if (cursor_info != last_cursor_info2)
2405 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2407 last_cursor_info2 = cursor_info;
2411 // ============================================================================
2413 // ============================================================================
2415 void SDLOpenAudio(void)
2417 if (program.headless)
2420 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2422 Warn("SDL_InitSubSystem() failed: %s", SDL_GetError());
2427 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2428 AUDIO_NUM_CHANNELS_STEREO,
2429 setup.system.audio_fragment_size) < 0)
2431 Warn("Mix_OpenAudio() failed: %s", SDL_GetError());
2436 audio.sound_available = TRUE;
2437 audio.music_available = TRUE;
2438 audio.loops_available = TRUE;
2439 audio.sound_enabled = TRUE;
2441 // set number of available mixer channels
2442 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2443 audio.music_channel = MUSIC_CHANNEL;
2444 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2446 Mixer_InitChannels();
2449 void SDLCloseAudio(void)
2452 Mix_HaltChannel(-1);
2455 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2459 // ============================================================================
2461 // ============================================================================
2463 void SDLWaitEvent(Event *event)
2465 SDL_WaitEvent(event);
2468 void SDLCorrectRawMousePosition(int *x, int *y)
2470 if (sdl_renderer == NULL)
2473 // this corrects the raw mouse position for logical screen size within event
2474 // filters (correction done later by SDL library when handling mouse events)
2477 float scale_x, scale_y;
2479 SDL_RenderGetViewport(sdl_renderer, &viewport);
2480 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2482 *x = (int)(*x / scale_x);
2483 *y = (int)(*y / scale_y);
2490 // ============================================================================
2491 // joystick functions
2492 // ============================================================================
2494 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2495 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2496 static int sdl_js_axis[MAX_PLAYERS][2];
2497 static int sdl_js_button[MAX_PLAYERS][2];
2498 static boolean sdl_is_controller[MAX_PLAYERS];
2500 void SDLClearJoystickState(void)
2504 for (i = 0; i < MAX_PLAYERS; i++)
2506 for (j = 0; j < 2; j++)
2508 sdl_js_axis_raw[i][j] = -1;
2509 sdl_js_axis[i][j] = 0;
2510 sdl_js_button[i][j] = 0;
2515 boolean SDLOpenJoystick(int nr)
2517 if (nr < 0 || nr >= MAX_PLAYERS)
2520 sdl_is_controller[nr] = SDL_IsGameController(nr);
2523 Debug("joystick", "opening joystick %d (%s)",
2524 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2527 if (sdl_is_controller[nr])
2528 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2530 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2532 return (sdl_joystick[nr] != NULL);
2535 void SDLCloseJoystick(int nr)
2537 if (nr < 0 || nr >= MAX_PLAYERS)
2541 Debug("joystick", "closing joystick %d", nr);
2544 if (sdl_is_controller[nr])
2545 SDL_GameControllerClose(sdl_joystick[nr]);
2547 SDL_JoystickClose(sdl_joystick[nr]);
2549 sdl_joystick[nr] = NULL;
2552 boolean SDLCheckJoystickOpened(int nr)
2554 if (nr < 0 || nr >= MAX_PLAYERS)
2557 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2560 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2562 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2563 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2564 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2565 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2567 if (nr < 0 || nr >= MAX_PLAYERS)
2573 // prevent (slightly jittering, but centered) axis A from resetting axis B
2574 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2575 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2578 sdl_js_axis[nr][axis_id] = axis_value;
2579 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2582 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2584 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2585 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2586 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2587 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2588 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2589 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2590 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2591 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2594 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2595 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2596 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2597 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2598 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2599 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2600 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2601 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2603 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2604 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2605 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2606 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2607 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2609 if (nr < 0 || nr >= MAX_PLAYERS)
2612 if (button_id == -1)
2615 sdl_js_button[nr][button_id] = button_state;
2618 void HandleJoystickEvent(Event *event)
2620 // when using joystick, disable overlay touch buttons
2621 runtime.uses_touch_device = FALSE;
2623 switch (event->type)
2625 case SDL_CONTROLLERDEVICEADDED:
2627 Debug("joystick", "SDL_CONTROLLERDEVICEADDED: device %d added",
2628 event->cdevice.which);
2633 case SDL_CONTROLLERDEVICEREMOVED:
2635 Debug("joystick", "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2636 event->cdevice.which);
2641 case SDL_CONTROLLERAXISMOTION:
2643 Debug("joystick", "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2644 event->caxis.which, event->caxis.axis, event->caxis.value);
2646 setJoystickAxis(event->caxis.which,
2648 event->caxis.value);
2651 case SDL_CONTROLLERBUTTONDOWN:
2653 Debug("joystick", "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2654 event->cbutton.which, event->cbutton.button);
2656 setJoystickButton(event->cbutton.which,
2657 event->cbutton.button,
2661 case SDL_CONTROLLERBUTTONUP:
2663 Debug("joystick", "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2664 event->cbutton.which, event->cbutton.button);
2666 setJoystickButton(event->cbutton.which,
2667 event->cbutton.button,
2671 case SDL_JOYAXISMOTION:
2672 if (sdl_is_controller[event->jaxis.which])
2676 Debug("joystick", "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2677 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2679 if (event->jaxis.axis < 4)
2680 setJoystickAxis(event->jaxis.which,
2682 event->jaxis.value);
2685 case SDL_JOYBUTTONDOWN:
2686 if (sdl_is_controller[event->jaxis.which])
2690 Debug("joystick", "SDL_JOYBUTTONDOWN: device %d, button %d",
2691 event->jbutton.which, event->jbutton.button);
2693 if (event->jbutton.button < 4)
2694 setJoystickButton(event->jbutton.which,
2695 event->jbutton.button,
2699 case SDL_JOYBUTTONUP:
2700 if (sdl_is_controller[event->jaxis.which])
2704 Debug("joystick", "SDL_JOYBUTTONUP: device %d, button %d",
2705 event->jbutton.which, event->jbutton.button);
2707 if (event->jbutton.button < 4)
2708 setJoystickButton(event->jbutton.which,
2709 event->jbutton.button,
2718 void SDLInitJoysticks(void)
2720 static boolean sdl_joystick_subsystem_initialized = FALSE;
2721 boolean print_warning = !sdl_joystick_subsystem_initialized;
2722 char *mappings_file_base = getPath2(options.conf_directory,
2723 GAMECONTROLLER_BASENAME);
2724 char *mappings_file_user = getPath2(getMainUserGameDataDir(),
2725 GAMECONTROLLER_BASENAME);
2729 if (!sdl_joystick_subsystem_initialized)
2731 sdl_joystick_subsystem_initialized = TRUE;
2733 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2735 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2736 Fail("SDL_Init() failed: %s", SDL_GetError());
2738 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2740 // the included game controller base mappings should always be found
2741 if (num_mappings == -1)
2742 Warn("no game controller base mappings found");
2745 Debug("joystick", "%d game controller base mapping(s) added",
2749 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2752 // the personal game controller user mappings may or may not be found
2753 if (num_mappings == -1)
2754 Warn("no game controller user mappings found");
2756 Debug("joystick", , "%d game controller user mapping(s) added",
2759 Debug("joystick", "%d joystick(s) found:", SDL_NumJoysticks());
2762 checked_free(mappings_file_base);
2763 checked_free(mappings_file_user);
2766 for (i = 0; i < SDL_NumJoysticks(); i++)
2768 const char *name, *type;
2770 if (SDL_IsGameController(i))
2772 name = SDL_GameControllerNameForIndex(i);
2773 type = "game controller";
2777 name = SDL_JoystickNameForIndex(i);
2781 Debug("joystick", "- joystick %d (%s): '%s'",
2782 i, type, (name ? name : "(Unknown)"));
2787 // assign joysticks from configured to connected joystick for all players
2788 for (i = 0; i < MAX_PLAYERS; i++)
2790 // get configured joystick for this player
2791 char *device_name = setup.input[i].joy.device_name;
2792 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2794 if (joystick_nr >= SDL_NumJoysticks())
2796 if (setup.input[i].use_joystick && print_warning)
2797 Warn("cannot find joystick %d", joystick_nr);
2802 // store configured joystick number for each player
2803 joystick.nr[i] = joystick_nr;
2806 // now open all connected joysticks (regardless if configured or not)
2807 for (i = 0; i < SDL_NumJoysticks(); i++)
2809 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2810 if (SDLCheckJoystickOpened(i))
2811 SDLCloseJoystick(i);
2813 if (SDLOpenJoystick(i))
2814 joystick.status = JOYSTICK_ACTIVATED;
2815 else if (print_warning)
2816 Warn("cannot open joystick %d", i);
2819 SDLClearJoystickState();
2822 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2824 if (nr < 0 || nr >= MAX_PLAYERS)
2828 *x = sdl_js_axis[nr][0];
2830 *y = sdl_js_axis[nr][1];
2833 *b1 = sdl_js_button[nr][0];
2835 *b2 = sdl_js_button[nr][1];
2841 // ============================================================================
2842 // touch input overlay functions
2843 // ============================================================================
2845 #if defined(USE_TOUCH_INPUT_OVERLAY)
2846 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2849 int grid_xsize = overlay.grid_xsize;
2850 int grid_ysize = overlay.grid_ysize;
2853 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2854 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2856 for (x = 0; x < grid_xsize; x++)
2858 rect.x = (x + 0) * video.screen_width / grid_xsize;
2859 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2861 for (y = 0; y < grid_ysize; y++)
2863 rect.y = (y + 0) * video.screen_height / grid_ysize;
2864 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2866 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2867 SDL_RenderDrawRect(sdl_renderer, &rect);
2871 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2874 static void RenderFillRectangle(int x, int y, int width, int height)
2876 SDL_Rect rect = { x, y, width, height };
2878 SDL_RenderFillRect(sdl_renderer, &rect);
2881 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2883 static int alpha_direction = 0;
2884 static int alpha_highlight = 0;
2885 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2886 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2888 int grid_xsize = overlay.grid_xsize;
2889 int grid_ysize = overlay.grid_ysize;
2892 if (alpha == alpha_max)
2894 if (alpha_direction < 0)
2896 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2898 if (alpha_highlight == 0)
2899 alpha_direction = 1;
2903 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2905 if (alpha_highlight == alpha_max)
2906 alpha_direction = -1;
2911 alpha_direction = 1;
2912 alpha_highlight = alpha;
2915 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2917 for (x = 0; x < grid_xsize; x++)
2919 for (y = 0; y < grid_ysize; y++)
2921 int grid_button = overlay.grid_button[x][y];
2922 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2923 int alpha_draw = alpha;
2924 int outline_border = MV_NONE;
2925 int border_size = 2;
2926 boolean draw_outlined = setup.touch.draw_outlined;
2927 boolean draw_pressed = setup.touch.draw_pressed;
2929 if (grid_button == CHAR_GRID_BUTTON_NONE)
2932 if (grid_button == overlay.grid_button_highlight)
2934 draw_outlined = FALSE;
2935 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2938 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2941 draw_outlined = FALSE;
2943 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2946 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2948 rect.x = (x + 0) * video.screen_width / grid_xsize;
2949 rect.y = (y + 0) * video.screen_height / grid_ysize;
2950 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2951 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2953 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2955 rect.x += border_size;
2956 rect.w -= border_size;
2958 outline_border |= MV_LEFT;
2961 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2963 rect.w -= border_size;
2965 outline_border |= MV_RIGHT;
2968 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2970 rect.y += border_size;
2971 rect.h -= border_size;
2973 outline_border |= MV_UP;
2976 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2978 rect.h -= border_size;
2980 outline_border |= MV_DOWN;
2985 int rect_x = rect.x +
2986 (outline_border & MV_LEFT ? border_size : 0);
2987 int rect_w = rect.w -
2988 (outline_border & MV_LEFT ? border_size : 0) -
2989 (outline_border & MV_RIGHT ? border_size : 0);
2991 if (outline_border & MV_LEFT)
2992 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2994 if (outline_border & MV_RIGHT)
2995 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2996 border_size, rect.h);
2998 if (outline_border & MV_UP)
2999 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
3001 if (outline_border & MV_DOWN)
3002 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
3003 rect_w, border_size);
3007 SDL_RenderFillRect(sdl_renderer, &rect);
3012 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3015 static void DrawTouchInputOverlay(void)
3017 static boolean deactivated = TRUE;
3018 static boolean show_grid = FALSE;
3019 static int alpha = 0;
3020 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3021 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3022 boolean active = (overlay.enabled && overlay.active);
3024 if (!active && deactivated)
3029 if (alpha < alpha_max)
3030 alpha = MIN(alpha + alpha_step, alpha_max);
3032 deactivated = FALSE;
3036 alpha = MAX(0, alpha - alpha_step);
3042 if (overlay.show_grid)
3044 else if (deactivated)
3048 DrawTouchInputOverlay_ShowGrid(alpha);
3050 DrawTouchInputOverlay_ShowGridButtons(alpha);
3053 static void DrawTouchGadgetsOverlay(void)
3055 DrawGadgets_OverlayTouchButtons();