1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
19 #define ENABLE_UNUSED_CODE 0 // currently unused functions
21 #define DEBUG_JOYSTICKS 0
24 // ============================================================================
26 // ============================================================================
28 // SDL internal variables
29 static SDL_Window *sdl_window = NULL;
30 static SDL_Renderer *sdl_renderer = NULL;
31 static SDL_Texture *sdl_texture_stream = NULL;
32 static SDL_Texture *sdl_texture_target = NULL;
33 static boolean fullscreen_enabled = FALSE;
34 static boolean limit_screen_updates = FALSE;
37 // functions from SGE library
38 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
40 #if defined(USE_TOUCH_INPUT_OVERLAY)
41 // functions to draw overlay graphics for touch device input
42 static void DrawTouchInputOverlay(void);
43 static void DrawTouchGadgetsOverlay(void);
46 void SDLLimitScreenUpdates(boolean enable)
48 limit_screen_updates = enable;
51 static void FinalizeScreen(int draw_target)
53 // copy global animations to render target buffer, if defined (below border)
54 if (gfx.draw_global_anim_function != NULL)
55 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
57 // copy global masked border to render target buffer, if defined
58 if (gfx.draw_global_border_function != NULL)
59 gfx.draw_global_border_function(draw_target);
61 // copy global animations to render target buffer, if defined (above border)
62 if (gfx.draw_global_anim_function != NULL)
63 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
65 // copy tile selection cursor to render target buffer, if defined (above all)
66 if (gfx.draw_tile_cursor_function != NULL)
67 gfx.draw_tile_cursor_function(draw_target);
69 // copy global animations to render target buffer, if defined (mouse pointer)
70 if (gfx.draw_global_anim_function != NULL)
71 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_3);
74 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
79 static DelayCounter update_screen_delay = { 50 }; // (milliseconds)
80 SDL_Surface *screen = backbuffer->surface;
82 if (limit_screen_updates &&
83 !DelayReached(&update_screen_delay))
86 LimitScreenUpdates(FALSE);
90 static int LastFrameCounter = 0;
91 boolean changed = (FrameCounter != LastFrameCounter);
93 Debug("internal:frame", "FrameCounter == %d [%s]", FrameCounter,
94 (changed ? "-" : "SAME FRAME UPDATED"));
96 LastFrameCounter = FrameCounter;
105 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
106 gfx.final_screen_bitmap != NULL) // may not be initialized yet
108 // draw global animations using bitmaps instead of using textures
109 // to prevent texture scaling artefacts (this is potentially slower)
111 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
112 gfx.win_xsize, gfx.win_ysize, 0, 0);
114 FinalizeScreen(DRAW_TO_SCREEN);
116 screen = gfx.final_screen_bitmap->surface;
118 // force full window redraw
122 SDL_Texture *sdl_texture = sdl_texture_stream;
124 // deactivate use of target texture if render targets are not supported
125 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
126 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
127 sdl_texture_target == NULL)
128 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
130 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
131 sdl_texture = sdl_texture_target;
135 int bytes_x = screen->pitch / video.width;
136 int bytes_y = screen->pitch;
138 SDL_UpdateTexture(sdl_texture, rect,
139 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
144 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
147 int xoff = video.screen_xoffset;
148 int yoff = video.screen_yoffset;
149 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
150 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
151 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
153 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
154 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
155 dst_rect2 = &dst_rect_screen;
157 dst_rect1 = &dst_rect_screen;
159 #if defined(HAS_SCREEN_KEYBOARD)
160 SDL_Rect src_rect_up = { 0, 0, video.width, video.height };
161 SDL_Rect dst_rect_up = dst_rect_screen;
163 if (video.shifted_up || video.shifted_up_delay.count)
165 int time_current = SDL_GetTicks();
166 int pos = video.shifted_up_pos;
167 int pos_last = video.shifted_up_pos_last;
169 if (!DelayReachedExt(&video.shifted_up_delay, time_current))
171 int delay_count = time_current - video.shifted_up_delay.count;
172 int delay_value = video.shifted_up_delay.value;
174 pos = pos_last + (pos - pos_last) * delay_count / delay_value;
178 video.shifted_up_pos_last = pos;
179 video.shifted_up_delay.count = 0;
183 src_rect_up.h = video.height - pos;
184 dst_rect_up.h = video.height - pos;
186 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
187 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
189 src_rect2 = &src_rect_up;
190 dst_rect2 = &dst_rect_up;
194 src_rect1 = &src_rect_up;
195 dst_rect1 = &dst_rect_up;
200 // clear render target buffer
201 SDL_RenderClear(sdl_renderer);
203 // set renderer to use target texture for rendering
204 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
205 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
206 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
208 // copy backbuffer texture to render target buffer
209 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
210 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
212 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
213 FinalizeScreen(DRAW_TO_SCREEN);
215 // when using target texture, copy it to screen buffer
216 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
217 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
219 SDL_SetRenderTarget(sdl_renderer, NULL);
220 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
223 #if defined(USE_TOUCH_INPUT_OVERLAY)
224 // draw overlay graphics for touch device input, if needed
225 DrawTouchInputOverlay();
227 // draw overlay gadgets for touch device input, if needed
228 DrawTouchGadgetsOverlay();
231 // global synchronization point of the game to align video frame delay
232 if (with_frame_delay)
233 WaitUntilDelayReached(&video.frame_delay);
235 video.frame_counter++;
237 // show render target buffer on screen
238 SDL_RenderPresent(sdl_renderer);
241 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
243 PumpEvents(); // execute event filter actions while waiting
245 UpdateScreenExt(rect, TRUE);
248 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
250 UpdateScreenExt(rect, FALSE);
253 void Delay_WithScreenUpdates(unsigned int delay)
255 unsigned int time_current = SDL_GetTicks();
256 unsigned int time_delayed = time_current + delay;
258 while (time_current < time_delayed)
260 // updating the screen contains waiting for frame delay (non-busy)
261 UpdateScreen_WithFrameDelay(NULL);
263 time_current = SDL_GetTicks();
267 static void SDLSetWindowIcon(char *basename)
269 // (setting the window icon on Mac OS X would replace the high-quality
270 // dock icon with the currently smaller (and uglier) icon from file)
272 #if !defined(PLATFORM_MAC)
273 char *filename = getCustomImageFilename(basename);
274 SDL_Surface *surface;
276 if (filename == NULL)
278 Warn("SDLSetWindowIcon(): cannot find file '%s'", basename);
283 if ((surface = IMG_Load(filename)) == NULL)
285 Warn("IMG_Load('%s') failed: %s", basename, SDL_GetError());
290 // set transparent color
291 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
292 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
294 SDL_SetWindowIcon(sdl_window, surface);
298 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
299 SDL_PixelFormat *format2)
301 return (format1->format == format2->format &&
302 format1->BitsPerPixel == format2->BitsPerPixel &&
303 format1->BytesPerPixel == format2->BytesPerPixel &&
304 format1->Rmask == format2->Rmask &&
305 format1->Gmask == format2->Gmask &&
306 format1->Bmask == format2->Bmask);
309 static void SDLCopyColorKey(SDL_Surface *src_surface, SDL_Surface *dst_surface)
314 // check if source surface has a color key
315 if (SDL_GetColorKey(src_surface, &color_key) == 0)
317 // get RGB values of color key of source surface
318 SDL_GetRGB(color_key, src_surface->format, &r, &g, &b);
320 // get color key from RGB values in destination surface format
321 color_key = SDL_MapRGB(dst_surface->format, r, g, b);
323 // set color key in destination surface
324 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL, color_key);
328 // unset color key in destination surface
329 SDL_SetColorKey(dst_surface, UNSET_TRANSPARENT_PIXEL, 0);
333 static boolean SDLHasColorKey(SDL_Surface *surface)
337 return (SDL_GetColorKey(surface, &color_key) == 0);
340 static boolean SDLHasAlpha(SDL_Surface *surface)
342 SDL_BlendMode blend_mode;
344 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
347 return (blend_mode == SDL_BLENDMODE_BLEND);
350 void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
352 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
354 SDL_SetSurfaceBlendMode(surface, blend_mode);
355 SDL_SetSurfaceAlphaMod(surface, alpha);
358 const char *SDLGetRendererName(void)
360 static SDL_RendererInfo renderer_info;
362 SDL_GetRendererInfo(sdl_renderer, &renderer_info);
364 return renderer_info.name;
367 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
369 SDL_PixelFormat format;
370 SDL_Surface *new_surface;
375 if (backbuffer && backbuffer->surface)
377 format = *backbuffer->surface->format;
378 format.Amask = surface->format->Amask; // keep alpha channel
382 format = *surface->format;
385 new_surface = SDL_ConvertSurface(surface, &format, 0);
387 if (new_surface == NULL)
388 Fail("SDL_ConvertSurface() failed: %s", SDL_GetError());
390 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
391 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
392 SDLCopyColorKey(surface, new_surface);
397 boolean SDLSetNativeSurface(SDL_Surface **surface)
399 SDL_Surface *new_surface;
401 if (surface == NULL ||
403 backbuffer == NULL ||
404 backbuffer->surface == NULL)
407 // if pixel format already optimized for destination surface, do nothing
408 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
411 new_surface = SDLGetNativeSurface(*surface);
413 SDL_FreeSurface(*surface);
415 *surface = new_surface;
420 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
422 if (program.headless)
425 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
428 Fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
433 void SDLCreateBitmapTextures(Bitmap *bitmap)
439 SDL_DestroyTexture(bitmap->texture);
440 if (bitmap->texture_masked)
441 SDL_DestroyTexture(bitmap->texture_masked);
443 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
444 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
447 void SDLFreeBitmapTextures(Bitmap *bitmap)
453 SDL_DestroyTexture(bitmap->texture);
454 if (bitmap->texture_masked)
455 SDL_DestroyTexture(bitmap->texture_masked);
457 bitmap->texture = NULL;
458 bitmap->texture_masked = NULL;
461 void SDLInitVideoDisplay(void)
463 // set hint to select render driver as specified in setup config file
464 if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
465 SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
467 // initialize SDL video
468 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
469 Fail("SDL_InitSubSystem() failed: %s", SDL_GetError());
471 // set default SDL depth
472 video.default_depth = 32; // (how to determine video depth in SDL2?)
474 // Code used with SDL 1.2:
475 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
478 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
480 if (program.headless)
483 video.window_scaling_percent = setup.window_scaling_percent;
484 video.window_scaling_quality = setup.window_scaling_quality;
486 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
488 // SDL 2.0: support for (desktop) fullscreen mode available
489 video.fullscreen_available = TRUE;
491 // open SDL video output device (window or fullscreen mode)
492 if (!SDLSetVideoMode(fullscreen))
493 Fail("setting video mode failed");
495 // !!! SDL2 can only set the window icon if the window already exists !!!
497 SDLSetWindowIcon(program.icon_filename);
499 // set window and icon title
503 static void SDLInitVideoBuffer_DrawBuffer(void)
505 /* SDL cannot directly draw to the visible video framebuffer like X11,
506 but always uses a backbuffer, which is then blitted to the visible
507 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
508 visible video framebuffer with 'SDL_Flip', if the hardware supports
509 this). Therefore do not use an additional backbuffer for drawing, but
510 use a symbolic buffer (distinguishable from the SDL backbuffer) called
511 'window', which indicates that the SDL backbuffer should be updated to
512 the visible video framebuffer when attempting to blit to it.
514 For convenience, it seems to be a good idea to create this symbolic
515 buffer 'window' at the same size as the SDL backbuffer. Although it
516 should never be drawn to directly, it would do no harm nevertheless. */
518 // create additional (symbolic) buffer for double-buffering
519 ReCreateBitmap(&window, video.width, video.height);
521 // create dummy drawing buffer for headless mode, if needed
522 if (program.headless)
523 ReCreateBitmap(&backbuffer, video.width, video.height);
526 void SDLInitVideoBuffer(boolean fullscreen)
528 SDLInitVideoBuffer_VideoBuffer(fullscreen);
529 SDLInitVideoBuffer_DrawBuffer();
532 static boolean SDLCreateScreen(boolean fullscreen)
534 SDL_Surface *new_surface = NULL;
536 int surface_flags_window = SURFACE_FLAGS;
537 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
540 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
542 video.vsync_mode = VSYNC_MODE_OFF;
544 if (!strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF))
546 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
547 video.vsync_mode = VSYNC_MODE_NORMAL;
550 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
551 _without_ enabling 2D/3D acceleration and/or guest additions installed,
552 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
553 it will try to use accelerated graphics and apparently fails miserably) */
554 int renderer_flags = SDL_RENDERER_SOFTWARE;
557 int width = video.width;
558 int height = video.height;
559 int screen_width = video.screen_width;
560 int screen_height = video.screen_height;
561 int surface_flags = (fullscreen ? surface_flags_fullscreen :
562 surface_flags_window);
563 int display_nr = options.display_nr;
565 // default window size is unscaled
566 video.window_width = screen_width;
567 video.window_height = screen_height;
569 // store if initial screen mode is fullscreen mode when changing screen size
570 video.fullscreen_initial = fullscreen;
572 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
574 video.window_width = window_scaling_factor * screen_width;
575 video.window_height = window_scaling_factor * screen_height;
577 if (sdl_texture_stream)
579 SDL_DestroyTexture(sdl_texture_stream);
580 sdl_texture_stream = NULL;
583 if (sdl_texture_target)
585 SDL_DestroyTexture(sdl_texture_target);
586 sdl_texture_target = NULL;
589 if (!(fullscreen && fullscreen_enabled))
593 SDL_DestroyRenderer(sdl_renderer);
599 SDL_SetWindowSize(sdl_window, video.window_width, video.window_height);
603 if (sdl_window == NULL)
604 sdl_window = SDL_CreateWindow(program.window_title,
605 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
606 SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
611 if (sdl_window != NULL)
613 if (sdl_renderer == NULL)
614 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
616 if (sdl_renderer != NULL)
618 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
619 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
621 // required for setting adaptive vsync when using OpenGL renderer
622 SDLSetScreenVsyncMode(setup.vsync_mode);
624 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
625 SDL_PIXELFORMAT_ARGB8888,
626 SDL_TEXTUREACCESS_STREAMING,
629 if (SDL_RenderTargetSupported(sdl_renderer))
630 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
631 SDL_PIXELFORMAT_ARGB8888,
632 SDL_TEXTUREACCESS_TARGET,
635 if (sdl_texture_stream != NULL)
637 // use SDL default values for RGB masks and no alpha channel
638 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
640 if (new_surface == NULL)
641 Warn("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
645 Warn("SDL_CreateTexture() failed: %s", SDL_GetError());
650 Warn("SDL_CreateRenderer() failed: %s", SDL_GetError());
655 Warn("SDL_CreateWindow() failed: %s", SDL_GetError());
658 SDLSetScreenProperties();
660 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
661 if (new_surface != NULL)
662 fullscreen_enabled = fullscreen;
664 if (backbuffer == NULL)
665 backbuffer = CreateBitmapStruct();
667 backbuffer->width = video.width;
668 backbuffer->height = video.height;
670 if (backbuffer->surface)
671 SDL_FreeSurface(backbuffer->surface);
673 backbuffer->surface = new_surface;
675 return (new_surface != NULL);
678 boolean SDLSetVideoMode(boolean fullscreen)
680 boolean success = FALSE;
684 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
686 // switch display to fullscreen mode, if available
687 success = SDLCreateScreen(TRUE);
691 // switching display to fullscreen mode failed -- do not try it again
692 video.fullscreen_available = FALSE;
696 video.fullscreen_enabled = TRUE;
700 if ((!fullscreen && video.fullscreen_enabled) || !success)
702 // switch display to window mode
703 success = SDLCreateScreen(FALSE);
707 // switching display to window mode failed -- should not happen
711 video.fullscreen_enabled = FALSE;
712 video.window_scaling_percent = setup.window_scaling_percent;
713 video.window_scaling_quality = setup.window_scaling_quality;
715 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
719 SDLRedrawWindow(); // map window
724 void SDLSetWindowTitle(void)
726 if (sdl_window == NULL)
729 SDL_SetWindowTitle(sdl_window, program.window_title);
732 void SDLSetWindowScaling(int window_scaling_percent)
734 if (sdl_window == NULL)
737 float window_scaling_factor = (float)window_scaling_percent / 100;
738 int new_window_width = (int)(window_scaling_factor * video.screen_width);
739 int new_window_height = (int)(window_scaling_factor * video.screen_height);
741 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
743 video.window_scaling_percent = window_scaling_percent;
744 video.window_width = new_window_width;
745 video.window_height = new_window_height;
750 void SDLSetWindowScalingQuality(char *window_scaling_quality)
752 SDL_Texture *new_texture;
754 if (sdl_texture_stream == NULL)
757 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
759 new_texture = SDL_CreateTexture(sdl_renderer,
760 SDL_PIXELFORMAT_ARGB8888,
761 SDL_TEXTUREACCESS_STREAMING,
762 video.width, video.height);
764 if (new_texture != NULL)
766 SDL_DestroyTexture(sdl_texture_stream);
768 sdl_texture_stream = new_texture;
771 if (SDL_RenderTargetSupported(sdl_renderer))
772 new_texture = SDL_CreateTexture(sdl_renderer,
773 SDL_PIXELFORMAT_ARGB8888,
774 SDL_TEXTUREACCESS_TARGET,
775 video.width, video.height);
779 if (new_texture != NULL)
781 SDL_DestroyTexture(sdl_texture_target);
783 sdl_texture_target = new_texture;
788 video.window_scaling_quality = window_scaling_quality;
791 void SDLSetWindowFullscreen(boolean fullscreen)
793 if (sdl_window == NULL)
796 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
798 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
799 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
801 // if screen size was changed in fullscreen mode, correct desktop window size
802 if (!fullscreen && video.fullscreen_initial)
804 SDLSetWindowScaling(setup.window_scaling_percent);
805 SDL_SetWindowPosition(sdl_window,
806 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr),
807 SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr));
809 video.fullscreen_initial = FALSE;
813 void SDLSetDisplaySize(void)
815 if (sdl_renderer != NULL)
819 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
821 video.display_width = w;
822 video.display_height = h;
825 Debug("video", "SDL renderer size: %d x %d",
826 video.display_width, video.display_height);
831 SDL_Rect display_bounds;
833 SDL_GetDisplayBounds(0, &display_bounds);
835 video.display_width = display_bounds.w;
836 video.display_height = display_bounds.h;
839 Debug("video", "SDL display size: %d x %d",
840 video.display_width, video.display_height);
845 void SDLSetScreenSizeAndOffsets(int width, int height)
847 // set default video screen size and offsets
848 video.screen_width = width;
849 video.screen_height = height;
850 video.screen_xoffset = 0;
851 video.screen_yoffset = 0;
853 #if defined(USE_COMPLETE_DISPLAY)
854 float ratio_video = (float) width / height;
855 float ratio_display = (float) video.display_width / video.display_height;
857 if (ratio_video != ratio_display)
859 // adjust drawable screen size to cover the whole device display
861 if (ratio_video < ratio_display)
862 video.screen_width *= ratio_display / ratio_video;
864 video.screen_height *= ratio_video / ratio_display;
866 video.screen_xoffset = (video.screen_width - width) / 2;
867 video.screen_yoffset = (video.screen_height - height) / 2;
870 Debug("video", "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
872 video.screen_width, video.screen_height,
873 ratio_video, ratio_display);
879 void SDLSetScreenSizeForRenderer(int width, int height)
881 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
884 void SDLSetScreenProperties(void)
887 SDLSetScreenSizeAndOffsets(video.width, video.height);
888 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
890 SetOverlayGridSizeAndButtons();
893 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
895 video.screen_rendering_mode =
896 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
897 SPECIAL_RENDERING_BITMAP :
898 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
899 SPECIAL_RENDERING_TARGET:
900 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
901 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
904 void SDLSetScreenVsyncMode(char *vsync_mode)
906 // changing vsync mode without re-creating renderer only supported by OpenGL
907 if (!strPrefixLower((char *)SDLGetRendererName(), "opengl"))
910 int interval = VSYNC_MODE_STR_TO_INT(vsync_mode);
911 int result = SDL_GL_SetSwapInterval(interval);
913 // if adaptive vsync requested, but not supported, retry with normal vsync
914 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
916 interval = VSYNC_MODE_NORMAL;
918 result = SDL_GL_SetSwapInterval(interval);
922 interval = VSYNC_MODE_OFF;
924 video.vsync_mode = interval;
927 void SDLRedrawWindow(void)
929 UpdateScreen_WithoutFrameDelay(NULL);
932 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
935 if (program.headless)
938 SDL_Surface *surface =
939 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
942 Fail("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
944 SDLSetNativeSurface(&surface);
946 bitmap->surface = surface;
949 void SDLFreeBitmapPointers(Bitmap *bitmap)
952 SDL_FreeSurface(bitmap->surface);
953 if (bitmap->surface_masked)
954 SDL_FreeSurface(bitmap->surface_masked);
956 bitmap->surface = NULL;
957 bitmap->surface_masked = NULL;
960 SDL_DestroyTexture(bitmap->texture);
961 if (bitmap->texture_masked)
962 SDL_DestroyTexture(bitmap->texture_masked);
964 bitmap->texture = NULL;
965 bitmap->texture_masked = NULL;
968 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
969 int src_x, int src_y, int width, int height,
970 int dst_x, int dst_y, int mask_mode)
972 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
973 SDL_Rect src_rect, dst_rect;
985 // if (src_bitmap != backbuffer || dst_bitmap != window)
986 if (!(src_bitmap == backbuffer && dst_bitmap == window))
987 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
988 src_bitmap->surface_masked : src_bitmap->surface),
989 &src_rect, real_dst_bitmap->surface, &dst_rect);
991 if (dst_bitmap == window)
992 UpdateScreen_WithFrameDelay(&dst_rect);
995 void SDLBlitTexture(Bitmap *bitmap,
996 int src_x, int src_y, int width, int height,
997 int dst_x, int dst_y, int mask_mode)
999 SDL_Texture *texture;
1004 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1006 if (texture == NULL)
1012 src_rect.h = height;
1017 dst_rect.h = height;
1019 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1022 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1025 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1033 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1035 if (dst_bitmap == window)
1036 UpdateScreen_WithFrameDelay(&rect);
1039 void PrepareFadeBitmap(int draw_target)
1041 Bitmap *fade_bitmap =
1042 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1043 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1045 if (fade_bitmap == NULL)
1048 // copy backbuffer to fading buffer
1049 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1051 // add border and animations to fading buffer
1052 FinalizeScreen(draw_target);
1055 void SDLFadeRectangle(int x, int y, int width, int height,
1056 int fade_mode, int fade_delay, int post_delay,
1057 void (*draw_border_function)(void))
1059 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1060 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1061 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1062 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1063 SDL_Surface *surface_screen = backbuffer->surface;
1064 SDL_Rect src_rect, dst_rect;
1066 int src_x = x, src_y = y;
1067 int dst_x = x, dst_y = y;
1068 unsigned int time_last, time_current;
1070 // store function for drawing global masked border
1071 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1073 // deactivate drawing of global border while fading, if needed
1074 if (draw_border_function == NULL)
1075 gfx.draw_global_border_function = NULL;
1080 src_rect.h = height;
1084 dst_rect.w = width; // (ignored)
1085 dst_rect.h = height; // (ignored)
1087 dst_rect2 = dst_rect;
1089 // before fading in, store backbuffer (without animation graphics)
1090 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1091 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1093 // copy source and target surfaces to temporary surfaces for fading
1094 if (fade_mode & FADE_TYPE_TRANSFORM)
1096 // (source and target fading buffer already prepared)
1098 else if (fade_mode & FADE_TYPE_FADE_IN)
1100 // (target fading buffer already prepared)
1101 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1103 else // FADE_TYPE_FADE_OUT
1105 // (source fading buffer already prepared)
1106 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1109 time_current = SDL_GetTicks();
1111 if (fade_delay <= 0)
1113 // immediately draw final target frame without delay
1114 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1118 // when fading without delay, also skip post delay
1122 if (fade_mode == FADE_MODE_MELT)
1124 boolean done = FALSE;
1125 int melt_pixels = 2;
1126 int melt_columns = width / melt_pixels;
1127 int ypos[melt_columns];
1128 int max_steps = height / 8 + 32;
1133 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1135 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1137 ypos[0] = -GetSimpleRandom(16);
1139 for (i = 1 ; i < melt_columns; i++)
1141 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1143 ypos[i] = ypos[i - 1] + r;
1156 time_last = time_current;
1157 time_current = SDL_GetTicks();
1158 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1159 steps_final = MIN(MAX(0, steps), max_steps);
1163 done = (steps_done >= steps_final);
1165 for (i = 0 ; i < melt_columns; i++)
1173 else if (ypos[i] < height)
1178 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1180 if (ypos[i] + dy >= height)
1181 dy = height - ypos[i];
1183 // copy part of (appearing) target surface to upper area
1184 src_rect.x = src_x + i * melt_pixels;
1185 // src_rect.y = src_y + ypos[i];
1187 src_rect.w = melt_pixels;
1189 src_rect.h = ypos[i] + dy;
1191 dst_rect.x = dst_x + i * melt_pixels;
1192 // dst_rect.y = dst_y + ypos[i];
1195 if (steps_done >= steps_final)
1196 SDL_BlitSurface(surface_target, &src_rect,
1197 surface_screen, &dst_rect);
1201 // copy part of (disappearing) source surface to lower area
1202 src_rect.x = src_x + i * melt_pixels;
1204 src_rect.w = melt_pixels;
1205 src_rect.h = height - ypos[i];
1207 dst_rect.x = dst_x + i * melt_pixels;
1208 dst_rect.y = dst_y + ypos[i];
1210 if (steps_done >= steps_final)
1211 SDL_BlitSurface(surface_source, &src_rect,
1212 surface_screen, &dst_rect);
1218 src_rect.x = src_x + i * melt_pixels;
1220 src_rect.w = melt_pixels;
1221 src_rect.h = height;
1223 dst_rect.x = dst_x + i * melt_pixels;
1226 if (steps_done >= steps_final)
1227 SDL_BlitSurface(surface_target, &src_rect,
1228 surface_screen, &dst_rect);
1232 if (steps_done >= steps_final)
1234 if (draw_border_function != NULL)
1235 draw_border_function();
1237 UpdateScreen_WithFrameDelay(&dst_rect2);
1239 if (PendingEscapeKeyEvent())
1244 else if (fade_mode == FADE_MODE_CURTAIN)
1248 int xx_size = width / 2;
1250 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1252 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1254 for (xx = 0; xx < xx_size;)
1256 time_last = time_current;
1257 time_current = SDL_GetTicks();
1258 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1259 xx_final = MIN(MAX(0, xx), xx_size);
1264 src_rect.h = height;
1269 // draw new (target) image to screen buffer
1270 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1272 if (xx_final < xx_size)
1274 src_rect.w = xx_size - xx_final;
1275 src_rect.h = height;
1277 // draw old (source) image to screen buffer (left side)
1279 src_rect.x = src_x + xx_final;
1282 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1284 // draw old (source) image to screen buffer (right side)
1286 src_rect.x = src_x + xx_size;
1287 dst_rect.x = dst_x + xx_size + xx_final;
1289 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1292 if (draw_border_function != NULL)
1293 draw_border_function();
1295 // only update the region of the screen that is affected from fading
1296 UpdateScreen_WithFrameDelay(&dst_rect2);
1298 if (PendingEscapeKeyEvent())
1302 else // fading in, fading out or cross-fading
1307 for (alpha = 0.0; alpha < 255.0;)
1309 time_last = time_current;
1310 time_current = SDL_GetTicks();
1311 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1312 alpha_final = MIN(MAX(0, alpha), 255);
1314 // draw existing (source) image to screen buffer
1315 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1317 // draw new (target) image to screen buffer using alpha blending
1318 SDLSetAlpha(surface_target, TRUE, alpha_final);
1319 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1321 if (draw_border_function != NULL)
1322 draw_border_function();
1324 // only update the region of the screen that is affected from fading
1325 UpdateScreen_WithFrameDelay(&dst_rect);
1327 if (PendingEscapeKeyEvent())
1333 Delay_WithScreenUpdates(post_delay);
1335 // restore function for drawing global masked border
1336 gfx.draw_global_border_function = draw_global_border_function;
1338 // after fading in, restore backbuffer (without animation graphics)
1339 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1340 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1343 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1344 int to_x, int to_y, Uint32 color)
1346 SDL_Surface *surface = dst_bitmap->surface;
1350 swap_numbers(&from_x, &to_x);
1353 swap_numbers(&from_y, &to_y);
1357 rect.w = (to_x - from_x + 1);
1358 rect.h = (to_y - from_y + 1);
1360 SDL_FillRect(surface, &rect, color);
1363 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1364 int to_x, int to_y, Uint32 color)
1366 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1369 #if ENABLE_UNUSED_CODE
1370 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1371 int num_points, Uint32 color)
1376 for (i = 0; i < num_points - 1; i++)
1378 for (x = 0; x < line_width; x++)
1380 for (y = 0; y < line_width; y++)
1382 int dx = x - line_width / 2;
1383 int dy = y - line_width / 2;
1385 if ((x == 0 && y == 0) ||
1386 (x == 0 && y == line_width - 1) ||
1387 (x == line_width - 1 && y == 0) ||
1388 (x == line_width - 1 && y == line_width - 1))
1391 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1392 points[i+1].x + dx, points[i+1].y + dy, color);
1399 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1401 SDL_Surface *surface = src_bitmap->surface;
1403 switch (surface->format->BytesPerPixel)
1405 case 1: // assuming 8-bpp
1407 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1411 case 2: // probably 15-bpp or 16-bpp
1413 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1417 case 3: // slow 24-bpp mode; usually not used
1420 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1424 shift = surface->format->Rshift;
1425 color |= *(pix + shift / 8) >> shift;
1426 shift = surface->format->Gshift;
1427 color |= *(pix + shift / 8) >> shift;
1428 shift = surface->format->Bshift;
1429 color |= *(pix + shift / 8) >> shift;
1435 case 4: // probably 32-bpp
1437 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1446 // ============================================================================
1447 // The following functions were taken from the SGE library
1448 // (SDL Graphics Extension Library) by Anders Lindström
1449 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1450 // ============================================================================
1452 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1454 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1456 switch (surface->format->BytesPerPixel)
1461 *((Uint8 *)surface->pixels + y * surface->pitch + x) = color;
1467 // Probably 15-bpp or 16-bpp
1468 *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x) = color;
1474 // Slow 24-bpp mode, usually not used
1478 // Gack - slow, but endian correct
1479 pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1480 shift = surface->format->Rshift;
1481 *(pix + shift / 8) = color>>shift;
1482 shift = surface->format->Gshift;
1483 *(pix + shift / 8) = color>>shift;
1484 shift = surface->format->Bshift;
1485 *(pix + shift / 8) = color>>shift;
1492 *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x) = color;
1500 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1501 Uint8 R, Uint8 G, Uint8 B)
1503 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1506 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1508 *((Uint8 *)surface->pixels + y * surface->pitch + x) = color;
1511 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1513 *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x) = color;
1516 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1521 // Gack - slow, but endian correct
1522 pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1523 shift = surface->format->Rshift;
1524 *(pix + shift / 8) = color>>shift;
1525 shift = surface->format->Gshift;
1526 *(pix + shift / 8) = color>>shift;
1527 shift = surface->format->Bshift;
1528 *(pix + shift / 8) = color>>shift;
1531 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1533 *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x) = color;
1536 static void _PutPixelX(SDL_Surface *dest, Sint16 x, Sint16 y, Uint32 color)
1538 switch (dest->format->BytesPerPixel)
1541 *((Uint8 *)dest->pixels + y * dest->pitch + x) = color;
1545 *((Uint16 *)dest->pixels + y * dest->pitch / 2 + x) = color;
1549 _PutPixel24(dest, x, y, color);
1553 *((Uint32 *)dest->pixels + y * dest->pitch / 4 + x) = color;
1559 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1561 if (SDL_MUSTLOCK(surface))
1563 if (SDL_LockSurface(surface) < 0)
1569 _PutPixel(surface, x, y, color);
1571 if (SDL_MUSTLOCK(surface))
1573 SDL_UnlockSurface(surface);
1578 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1579 Uint8 r, Uint8 g, Uint8 b)
1581 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1584 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1586 if (y >= 0 && y <= dest->h - 1)
1588 switch (dest->format->BytesPerPixel)
1591 return y * dest->pitch;
1595 return y * dest->pitch / 2;
1599 return y * dest->pitch;
1603 return y * dest->pitch / 4;
1611 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1614 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1616 switch (surface->format->BytesPerPixel)
1621 *((Uint8 *)surface->pixels + ypitch + x) = color;
1627 // Probably 15-bpp or 16-bpp
1628 *((Uint16 *)surface->pixels + ypitch + x) = color;
1634 // Slow 24-bpp mode, usually not used
1638 // Gack - slow, but endian correct
1639 pix = (Uint8 *)surface->pixels + ypitch + x * 3;
1640 shift = surface->format->Rshift;
1641 *(pix + shift / 8) = color>>shift;
1642 shift = surface->format->Gshift;
1643 *(pix + shift / 8) = color>>shift;
1644 shift = surface->format->Bshift;
1645 *(pix + shift / 8) = color>>shift;
1652 *((Uint32 *)surface->pixels + ypitch + x) = color;
1659 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1664 if (SDL_MUSTLOCK(Surface))
1666 if (SDL_LockSurface(Surface) < 0)
1680 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1684 if (x2 > Surface->w - 1)
1685 x2 = Surface->w - 1;
1692 SDL_FillRect(Surface, &l, Color);
1694 if (SDL_MUSTLOCK(Surface))
1696 SDL_UnlockSurface(Surface);
1700 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1701 Uint8 R, Uint8 G, Uint8 B)
1703 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1706 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1719 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1723 if (x2 > Surface->w - 1)
1724 x2 = Surface->w - 1;
1731 SDL_FillRect(Surface, &l, Color);
1734 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1739 if (SDL_MUSTLOCK(Surface))
1741 if (SDL_LockSurface(Surface) < 0)
1755 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1759 if (y2 > Surface->h - 1)
1760 y2 = Surface->h - 1;
1767 SDL_FillRect(Surface, &l, Color);
1769 if (SDL_MUSTLOCK(Surface))
1771 SDL_UnlockSurface(Surface);
1775 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1776 Uint8 R, Uint8 G, Uint8 B)
1778 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1781 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1794 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1798 if (y2 > Surface->h - 1)
1799 y2 = Surface->h - 1;
1806 SDL_FillRect(Surface, &l, Color);
1810 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1811 Sint16 x2, Sint16 y2, Uint32 Color,
1812 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1815 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1820 sdx = (dx < 0) ? -1 : 1;
1821 sdy = (dy < 0) ? -1 : 1;
1833 for (x = 0; x < dx; x++)
1835 Callback(Surface, px, py, Color);
1849 for (y = 0; y < dy; y++)
1851 Callback(Surface, px, py, Color);
1866 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1867 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1868 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1871 sge_DoLine(Surface, X1, Y1, X2, Y2,
1872 SDL_MapRGB(Surface->format, R, G, B), Callback);
1876 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1879 if (SDL_MUSTLOCK(Surface))
1881 if (SDL_LockSurface(Surface) < 0)
1886 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1888 // unlock the display
1889 if (SDL_MUSTLOCK(Surface))
1891 SDL_UnlockSurface(Surface);
1896 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1897 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1899 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1903 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1905 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1909 // ----------------------------------------------------------------------------
1910 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1911 // ----------------------------------------------------------------------------
1913 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1914 int src_x, int src_y, int width, int height,
1915 int dst_x, int dst_y)
1919 for (y = 0; y < height; y++)
1921 for (x = 0; x < width; x++)
1923 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1925 if (pixel != BLACK_PIXEL)
1926 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1932 // ============================================================================
1933 // The following functions were taken from the SDL_gfx library version 2.0.3
1934 // (Rotozoomer) by Andreas Schiffler
1935 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1936 // ============================================================================
1938 // ----------------------------------------------------------------------------
1941 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1942 // ----------------------------------------------------------------------------
1952 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1955 tColorRGBA *sp, *csp, *dp;
1959 sp = csp = (tColorRGBA *) src->pixels;
1960 dp = (tColorRGBA *) dst->pixels;
1961 dgap = dst->pitch - dst->w * 4;
1963 for (y = 0; y < dst->h; y++)
1967 for (x = 0; x < dst->w; x++)
1969 tColorRGBA *sp0 = sp;
1970 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1971 tColorRGBA *sp00 = &sp0[0];
1972 tColorRGBA *sp01 = &sp0[1];
1973 tColorRGBA *sp10 = &sp1[0];
1974 tColorRGBA *sp11 = &sp1[1];
1977 // create new color pixel from all four source color pixels
1978 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1979 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1980 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1981 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1986 // advance source pointers
1989 // advance destination pointer
1993 // advance source pointer
1994 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1996 // advance destination pointers
1997 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2003 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2005 int x, y, *sax, *say, *csax, *csay;
2007 tColorRGBA *sp, *csp, *csp0, *dp;
2010 // use specialized zoom function when scaling down to exactly half size
2011 if (src->w == 2 * dst->w &&
2012 src->h == 2 * dst->h)
2013 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2016 sx = (float) src->w / (float) dst->w;
2017 sy = (float) src->h / (float) dst->h;
2019 // allocate memory for row increments
2020 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2021 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2023 // precalculate row increments
2024 for (x = 0; x <= dst->w; x++)
2025 *csax++ = (int)(sx * x);
2027 for (y = 0; y <= dst->h; y++)
2028 *csay++ = (int)(sy * y);
2031 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2032 dp = (tColorRGBA *) dst->pixels;
2033 dgap = dst->pitch - dst->w * 4;
2036 for (y = 0; y < dst->h; y++)
2041 for (x = 0; x < dst->w; x++)
2046 // advance source pointers
2050 // advance destination pointer
2054 // advance source pointer
2056 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2058 // advance destination pointers
2059 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2068 // ----------------------------------------------------------------------------
2071 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2072 // ----------------------------------------------------------------------------
2074 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2076 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2077 Uint8 *sp, *dp, *csp;
2081 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2082 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2084 // allocate memory for row increments
2085 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2086 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2088 // precalculate row increments
2091 for (x = 0; x < dst->w; x++)
2094 *csax = (csx >> 16);
2101 for (y = 0; y < dst->h; y++)
2104 *csay = (csy >> 16);
2111 for (x = 0; x < dst->w; x++)
2119 for (y = 0; y < dst->h; y++)
2126 sp = csp = (Uint8 *) src->pixels;
2127 dp = (Uint8 *) dst->pixels;
2128 dgap = dst->pitch - dst->w;
2132 for (y = 0; y < dst->h; y++)
2136 for (x = 0; x < dst->w; x++)
2141 // advance source pointers
2145 // advance destination pointer
2149 // advance source pointer (for row)
2150 csp += ((*csay) * src->pitch);
2153 // advance destination pointers
2163 // ----------------------------------------------------------------------------
2166 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2167 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2168 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2169 // into a 32bit RGBA format on the fly.
2170 // ----------------------------------------------------------------------------
2172 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2174 SDL_Surface *zoom_src = NULL;
2175 SDL_Surface *zoom_dst = NULL;
2176 boolean is_converted = FALSE;
2183 // determine if source surface is 32 bit or 8 bit
2184 is_32bit = (src->format->BitsPerPixel == 32);
2186 if (is_32bit || src->format->BitsPerPixel == 8)
2188 // use source surface 'as is'
2193 // new source surface is 32 bit with a defined RGB ordering
2194 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2195 0x000000ff, 0x0000ff00, 0x00ff0000,
2196 (src->format->Amask ? 0xff000000 : 0));
2197 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2199 is_converted = TRUE;
2202 // allocate surface to completely contain the zoomed surface
2205 // target surface is 32 bit with source RGBA/ABGR ordering
2206 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2207 zoom_src->format->Rmask,
2208 zoom_src->format->Gmask,
2209 zoom_src->format->Bmask,
2210 zoom_src->format->Amask);
2214 // target surface is 8 bit
2215 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2219 // lock source surface
2220 SDL_LockSurface(zoom_src);
2222 // check which kind of surface we have
2225 // call the 32 bit transformation routine to do the zooming
2226 zoomSurfaceRGBA(zoom_src, zoom_dst);
2231 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2232 zoom_dst->format->palette->colors[i] =
2233 zoom_src->format->palette->colors[i];
2234 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2236 // call the 8 bit transformation routine to do the zooming
2237 zoomSurfaceY(zoom_src, zoom_dst);
2240 // unlock source surface
2241 SDL_UnlockSurface(zoom_src);
2243 // free temporary surface
2245 SDL_FreeSurface(zoom_src);
2247 // return destination surface
2251 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2253 SDL_Surface *new_surface;
2255 if (surface == NULL)
2258 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2259 Fail("SDLGetNativeSurface() failed");
2261 // remove alpha channel from native non-transparent surface, if defined
2262 SDLSetAlpha(new_surface, FALSE, 0);
2264 // remove transparent color from native non-transparent surface, if defined
2265 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2270 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2272 Bitmap *dst_bitmap = CreateBitmapStruct();
2273 SDL_Surface *src_surface = src_bitmap->surface_masked;
2274 SDL_Surface *dst_surface;
2276 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2277 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2279 dst_bitmap->width = dst_width;
2280 dst_bitmap->height = dst_height;
2282 // create zoomed temporary surface from source surface
2283 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2285 // create native format destination surface from zoomed temporary surface
2286 SDLSetNativeSurface(&dst_surface);
2288 // set color key for zoomed surface from source surface, if defined
2289 if (SDLHasColorKey(src_surface))
2290 SDLCopyColorKey(src_surface, dst_surface);
2292 // create native non-transparent surface for opaque blitting
2293 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2295 // set native transparent surface for masked blitting
2296 dst_bitmap->surface_masked = dst_surface;
2302 // ============================================================================
2303 // load image to bitmap
2304 // ============================================================================
2306 Bitmap *SDLLoadImage(char *filename)
2308 Bitmap *new_bitmap = CreateBitmapStruct();
2309 SDL_Surface *sdl_image_tmp;
2311 if (program.headless)
2313 // prevent sanity check warnings at later stage
2314 new_bitmap->width = new_bitmap->height = 1;
2319 print_timestamp_init("SDLLoadImage");
2321 print_timestamp_time(getBaseNamePtr(filename));
2323 // load image to temporary surface
2324 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2325 Fail("IMG_Load('%s') failed: %s", getBaseNamePtr(filename), SDL_GetError());
2327 print_timestamp_time("IMG_Load");
2329 UPDATE_BUSY_STATE();
2331 // create native non-transparent surface for current image
2332 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2333 Fail("SDLGetOpaqueSurface() failed");
2335 print_timestamp_time("SDLGetNativeSurface (opaque)");
2337 UPDATE_BUSY_STATE();
2339 // set black pixel to transparent if no alpha channel / transparent color
2340 if (!SDLHasAlpha(sdl_image_tmp) &&
2341 !SDLHasColorKey(sdl_image_tmp))
2342 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2343 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2345 // create native transparent surface for current image
2346 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2347 Fail("SDLGetNativeSurface() failed");
2349 print_timestamp_time("SDLGetNativeSurface (masked)");
2351 UPDATE_BUSY_STATE();
2353 // free temporary surface
2354 SDL_FreeSurface(sdl_image_tmp);
2356 new_bitmap->width = new_bitmap->surface->w;
2357 new_bitmap->height = new_bitmap->surface->h;
2359 print_timestamp_done("SDLLoadImage");
2365 // ----------------------------------------------------------------------------
2366 // custom cursor fuctions
2367 // ----------------------------------------------------------------------------
2369 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2371 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2372 cursor_info->width, cursor_info->height,
2373 cursor_info->hot_x, cursor_info->hot_y);
2376 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2378 static struct MouseCursorInfo *last_cursor_info = NULL;
2379 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2380 static SDL_Cursor *cursor_default = NULL;
2381 static SDL_Cursor *cursor_current = NULL;
2383 // if invoked for the first time, store the SDL default cursor
2384 if (cursor_default == NULL)
2385 cursor_default = SDL_GetCursor();
2387 // only create new cursor if cursor info (custom only) has changed
2388 if (cursor_info != NULL && cursor_info != last_cursor_info)
2390 cursor_current = create_cursor(cursor_info);
2391 last_cursor_info = cursor_info;
2394 // only set new cursor if cursor info (custom or NULL) has changed
2395 if (cursor_info != last_cursor_info2)
2396 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2398 last_cursor_info2 = cursor_info;
2402 // ============================================================================
2404 // ============================================================================
2406 void SDLOpenAudio(void)
2408 if (program.headless)
2411 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2413 Warn("SDL_InitSubSystem() failed: %s", SDL_GetError());
2418 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2419 AUDIO_NUM_CHANNELS_STEREO,
2420 setup.system.audio_fragment_size) < 0)
2422 Warn("Mix_OpenAudio() failed: %s", SDL_GetError());
2427 audio.sound_available = TRUE;
2428 audio.music_available = TRUE;
2429 audio.loops_available = TRUE;
2430 audio.sound_enabled = TRUE;
2432 // set number of available mixer channels
2433 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2434 audio.music_channel = MUSIC_CHANNEL;
2435 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2437 Mixer_InitChannels();
2440 void SDLCloseAudio(void)
2443 Mix_HaltChannel(-1);
2446 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2450 // ============================================================================
2452 // ============================================================================
2454 void SDLWaitEvent(Event *event)
2456 SDL_WaitEvent(event);
2459 void SDLCorrectRawMousePosition(int *x, int *y)
2461 if (sdl_renderer == NULL)
2464 // this corrects the raw mouse position for logical screen size within event
2465 // filters (correction done later by SDL library when handling mouse events)
2468 float scale_x, scale_y;
2470 SDL_RenderGetViewport(sdl_renderer, &viewport);
2471 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2473 *x = (int)(*x / scale_x);
2474 *y = (int)(*y / scale_y);
2481 // ============================================================================
2482 // joystick functions
2483 // ============================================================================
2485 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2486 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2487 static int sdl_js_axis[MAX_PLAYERS][2];
2488 static int sdl_js_button[MAX_PLAYERS][2];
2489 static boolean sdl_is_controller[MAX_PLAYERS];
2491 void SDLClearJoystickState(void)
2495 for (i = 0; i < MAX_PLAYERS; i++)
2497 for (j = 0; j < 2; j++)
2499 sdl_js_axis_raw[i][j] = -1;
2500 sdl_js_axis[i][j] = 0;
2501 sdl_js_button[i][j] = 0;
2506 boolean SDLOpenJoystick(int nr)
2508 if (nr < 0 || nr >= MAX_PLAYERS)
2511 sdl_is_controller[nr] = SDL_IsGameController(nr);
2514 Debug("joystick", "opening joystick %d (%s)",
2515 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2518 if (sdl_is_controller[nr])
2519 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2521 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2523 return (sdl_joystick[nr] != NULL);
2526 void SDLCloseJoystick(int nr)
2528 if (nr < 0 || nr >= MAX_PLAYERS)
2532 Debug("joystick", "closing joystick %d", nr);
2535 if (sdl_is_controller[nr])
2536 SDL_GameControllerClose(sdl_joystick[nr]);
2538 SDL_JoystickClose(sdl_joystick[nr]);
2540 sdl_joystick[nr] = NULL;
2543 boolean SDLCheckJoystickOpened(int nr)
2545 if (nr < 0 || nr >= MAX_PLAYERS)
2548 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2551 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2553 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2554 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2555 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2556 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2558 if (nr < 0 || nr >= MAX_PLAYERS)
2564 // prevent (slightly jittering, but centered) axis A from resetting axis B
2565 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2566 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2569 sdl_js_axis[nr][axis_id] = axis_value;
2570 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2573 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2575 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2576 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2577 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2578 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2579 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2580 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2581 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2582 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2585 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2586 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2587 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2588 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2589 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2590 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2591 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2592 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2594 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2595 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2596 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2597 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2598 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2600 if (nr < 0 || nr >= MAX_PLAYERS)
2603 if (button_id == -1)
2606 sdl_js_button[nr][button_id] = button_state;
2609 void HandleJoystickEvent(Event *event)
2611 // when using joystick, disable overlay touch buttons
2612 runtime.uses_touch_device = FALSE;
2614 switch (event->type)
2616 case SDL_CONTROLLERDEVICEADDED:
2618 Debug("joystick", "SDL_CONTROLLERDEVICEADDED: device %d added",
2619 event->cdevice.which);
2624 case SDL_CONTROLLERDEVICEREMOVED:
2626 Debug("joystick", "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2627 event->cdevice.which);
2632 case SDL_CONTROLLERAXISMOTION:
2634 Debug("joystick", "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2635 event->caxis.which, event->caxis.axis, event->caxis.value);
2637 setJoystickAxis(event->caxis.which,
2639 event->caxis.value);
2642 case SDL_CONTROLLERBUTTONDOWN:
2644 Debug("joystick", "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2645 event->cbutton.which, event->cbutton.button);
2647 setJoystickButton(event->cbutton.which,
2648 event->cbutton.button,
2652 case SDL_CONTROLLERBUTTONUP:
2654 Debug("joystick", "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2655 event->cbutton.which, event->cbutton.button);
2657 setJoystickButton(event->cbutton.which,
2658 event->cbutton.button,
2662 case SDL_JOYAXISMOTION:
2663 if (sdl_is_controller[event->jaxis.which])
2667 Debug("joystick", "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2668 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2670 if (event->jaxis.axis < 4)
2671 setJoystickAxis(event->jaxis.which,
2673 event->jaxis.value);
2676 case SDL_JOYBUTTONDOWN:
2677 if (sdl_is_controller[event->jaxis.which])
2681 Debug("joystick", "SDL_JOYBUTTONDOWN: device %d, button %d",
2682 event->jbutton.which, event->jbutton.button);
2684 if (event->jbutton.button < 4)
2685 setJoystickButton(event->jbutton.which,
2686 event->jbutton.button,
2690 case SDL_JOYBUTTONUP:
2691 if (sdl_is_controller[event->jaxis.which])
2695 Debug("joystick", "SDL_JOYBUTTONUP: device %d, button %d",
2696 event->jbutton.which, event->jbutton.button);
2698 if (event->jbutton.button < 4)
2699 setJoystickButton(event->jbutton.which,
2700 event->jbutton.button,
2709 void SDLInitJoysticks(void)
2711 static boolean sdl_joystick_subsystem_initialized = FALSE;
2712 boolean print_warning = !sdl_joystick_subsystem_initialized;
2713 char *mappings_file_base = getPath2(options.conf_directory,
2714 GAMECONTROLLER_BASENAME);
2715 char *mappings_file_user = getPath2(getMainUserGameDataDir(),
2716 GAMECONTROLLER_BASENAME);
2720 if (!sdl_joystick_subsystem_initialized)
2722 sdl_joystick_subsystem_initialized = TRUE;
2724 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2726 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2727 Fail("SDL_Init() failed: %s", SDL_GetError());
2729 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2731 // the included game controller base mappings should always be found
2732 if (num_mappings == -1)
2733 Warn("no game controller base mappings found");
2736 Debug("joystick", "%d game controller base mapping(s) added",
2740 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2743 // the personal game controller user mappings may or may not be found
2744 if (num_mappings == -1)
2745 Warn("no game controller user mappings found");
2747 Debug("joystick", , "%d game controller user mapping(s) added",
2750 Debug("joystick", "%d joystick(s) found:", SDL_NumJoysticks());
2753 checked_free(mappings_file_base);
2754 checked_free(mappings_file_user);
2757 for (i = 0; i < SDL_NumJoysticks(); i++)
2759 const char *name, *type;
2761 if (SDL_IsGameController(i))
2763 name = SDL_GameControllerNameForIndex(i);
2764 type = "game controller";
2768 name = SDL_JoystickNameForIndex(i);
2772 Debug("joystick", "- joystick %d (%s): '%s'",
2773 i, type, (name ? name : "(Unknown)"));
2778 // assign joysticks from configured to connected joystick for all players
2779 for (i = 0; i < MAX_PLAYERS; i++)
2781 // get configured joystick for this player
2782 char *device_name = setup.input[i].joy.device_name;
2783 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2785 if (joystick_nr >= SDL_NumJoysticks())
2787 if (setup.input[i].use_joystick && print_warning)
2788 Warn("cannot find joystick %d", joystick_nr);
2793 // store configured joystick number for each player
2794 joystick.nr[i] = joystick_nr;
2797 // now open all connected joysticks (regardless if configured or not)
2798 for (i = 0; i < SDL_NumJoysticks(); i++)
2800 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2801 if (SDLCheckJoystickOpened(i))
2802 SDLCloseJoystick(i);
2804 if (SDLOpenJoystick(i))
2805 joystick.status = JOYSTICK_ACTIVATED;
2806 else if (print_warning)
2807 Warn("cannot open joystick %d", i);
2810 SDLClearJoystickState();
2813 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2815 if (nr < 0 || nr >= MAX_PLAYERS)
2819 *x = sdl_js_axis[nr][0];
2821 *y = sdl_js_axis[nr][1];
2824 *b1 = sdl_js_button[nr][0];
2826 *b2 = sdl_js_button[nr][1];
2832 // ============================================================================
2833 // touch input overlay functions
2834 // ============================================================================
2836 #if defined(USE_TOUCH_INPUT_OVERLAY)
2837 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2840 int grid_xsize = overlay.grid_xsize;
2841 int grid_ysize = overlay.grid_ysize;
2844 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2845 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2847 for (x = 0; x < grid_xsize; x++)
2849 rect.x = (x + 0) * video.screen_width / grid_xsize;
2850 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2852 for (y = 0; y < grid_ysize; y++)
2854 rect.y = (y + 0) * video.screen_height / grid_ysize;
2855 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2857 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2858 SDL_RenderDrawRect(sdl_renderer, &rect);
2862 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2865 static void RenderFillRectangle(int x, int y, int width, int height)
2867 SDL_Rect rect = { x, y, width, height };
2869 SDL_RenderFillRect(sdl_renderer, &rect);
2872 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2874 static int alpha_direction = 0;
2875 static int alpha_highlight = 0;
2876 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2877 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2879 int grid_xsize = overlay.grid_xsize;
2880 int grid_ysize = overlay.grid_ysize;
2883 if (alpha == alpha_max)
2885 if (alpha_direction < 0)
2887 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2889 if (alpha_highlight == 0)
2890 alpha_direction = 1;
2894 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2896 if (alpha_highlight == alpha_max)
2897 alpha_direction = -1;
2902 alpha_direction = 1;
2903 alpha_highlight = alpha;
2906 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2908 for (x = 0; x < grid_xsize; x++)
2910 for (y = 0; y < grid_ysize; y++)
2912 int grid_button = overlay.grid_button[x][y];
2913 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2914 int alpha_draw = alpha;
2915 int outline_border = MV_NONE;
2916 int border_size = 2;
2917 boolean draw_outlined = setup.touch.draw_outlined;
2918 boolean draw_pressed = setup.touch.draw_pressed;
2920 if (grid_button == CHAR_GRID_BUTTON_NONE)
2923 if (grid_button == overlay.grid_button_highlight)
2925 draw_outlined = FALSE;
2926 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2929 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2932 draw_outlined = FALSE;
2934 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2937 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2939 rect.x = (x + 0) * video.screen_width / grid_xsize;
2940 rect.y = (y + 0) * video.screen_height / grid_ysize;
2941 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2942 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2944 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2946 rect.x += border_size;
2947 rect.w -= border_size;
2949 outline_border |= MV_LEFT;
2952 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2954 rect.w -= border_size;
2956 outline_border |= MV_RIGHT;
2959 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2961 rect.y += border_size;
2962 rect.h -= border_size;
2964 outline_border |= MV_UP;
2967 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2969 rect.h -= border_size;
2971 outline_border |= MV_DOWN;
2976 int rect_x = rect.x +
2977 (outline_border & MV_LEFT ? border_size : 0);
2978 int rect_w = rect.w -
2979 (outline_border & MV_LEFT ? border_size : 0) -
2980 (outline_border & MV_RIGHT ? border_size : 0);
2982 if (outline_border & MV_LEFT)
2983 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2985 if (outline_border & MV_RIGHT)
2986 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2987 border_size, rect.h);
2989 if (outline_border & MV_UP)
2990 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2992 if (outline_border & MV_DOWN)
2993 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
2994 rect_w, border_size);
2998 SDL_RenderFillRect(sdl_renderer, &rect);
3003 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3006 static void DrawTouchInputOverlay(void)
3008 static boolean deactivated = TRUE;
3009 static boolean show_grid = FALSE;
3010 static int alpha = 0;
3011 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3012 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3013 boolean active = (overlay.enabled && overlay.active);
3015 if (!active && deactivated)
3020 if (alpha < alpha_max)
3021 alpha = MIN(alpha + alpha_step, alpha_max);
3023 deactivated = FALSE;
3027 alpha = MAX(0, alpha - alpha_step);
3033 if (overlay.show_grid)
3035 else if (deactivated)
3039 DrawTouchInputOverlay_ShowGrid(alpha);
3041 DrawTouchInputOverlay_ShowGridButtons(alpha);
3044 static void DrawTouchGadgetsOverlay(void)
3046 DrawGadgets_OverlayTouchButtons();