1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
18 #define ENABLE_UNUSED_CODE 0 // currently unused functions
20 #define DEBUG_JOYSTICKS 0
23 // ============================================================================
25 // ============================================================================
27 // SDL internal variables
28 static SDL_Window *sdl_window = NULL;
29 static SDL_Renderer *sdl_renderer = NULL;
30 static SDL_Texture *sdl_texture_stream = NULL;
31 static SDL_Texture *sdl_texture_target = NULL;
32 static boolean fullscreen_enabled = FALSE;
33 static boolean limit_screen_updates = FALSE;
36 // functions from SGE library
37 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
39 #if defined(USE_TOUCH_INPUT_OVERLAY)
40 // functions to draw overlay graphics for touch device input
41 static void DrawTouchInputOverlay(void);
44 void SDLLimitScreenUpdates(boolean enable)
46 limit_screen_updates = enable;
49 static void FinalizeScreen(int draw_target)
51 // copy global animations to render target buffer, if defined (below border)
52 if (gfx.draw_global_anim_function != NULL)
53 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
55 // copy global masked border to render target buffer, if defined
56 if (gfx.draw_global_border_function != NULL)
57 gfx.draw_global_border_function(draw_target);
59 // copy global animations to render target buffer, if defined (above border)
60 if (gfx.draw_global_anim_function != NULL)
61 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
63 // copy tile selection cursor to render target buffer, if defined (above all)
64 if (gfx.draw_tile_cursor_function != NULL)
65 gfx.draw_tile_cursor_function(draw_target);
68 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
70 static unsigned int update_screen_delay = 0;
71 unsigned int update_screen_delay_value = 50; // (milliseconds)
72 SDL_Surface *screen = backbuffer->surface;
74 if (limit_screen_updates &&
75 !DelayReached(&update_screen_delay, update_screen_delay_value))
78 LimitScreenUpdates(FALSE);
82 static int LastFrameCounter = 0;
83 boolean changed = (FrameCounter != LastFrameCounter);
85 printf("::: FrameCounter == %d [%s]\n", FrameCounter,
86 (changed ? "-" : "SAME FRAME UPDATED"));
88 LastFrameCounter = FrameCounter;
97 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
98 gfx.final_screen_bitmap != NULL) // may not be initialized yet
100 // draw global animations using bitmaps instead of using textures
101 // to prevent texture scaling artefacts (this is potentially slower)
103 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
104 gfx.win_xsize, gfx.win_ysize, 0, 0);
106 FinalizeScreen(DRAW_TO_SCREEN);
108 screen = gfx.final_screen_bitmap->surface;
110 // force full window redraw
114 SDL_Texture *sdl_texture = sdl_texture_stream;
116 // deactivate use of target texture if render targets are not supported
117 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
118 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
119 sdl_texture_target == NULL)
120 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
122 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
123 sdl_texture = sdl_texture_target;
127 int bytes_x = screen->pitch / video.width;
128 int bytes_y = screen->pitch;
130 SDL_UpdateTexture(sdl_texture, rect,
131 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
136 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
139 int xoff = video.screen_xoffset;
140 int yoff = video.screen_yoffset;
141 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
142 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
143 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
145 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
146 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
147 dst_rect2 = &dst_rect_screen;
149 dst_rect1 = &dst_rect_screen;
151 #if defined(HAS_SCREEN_KEYBOARD)
152 if (video.shifted_up || video.shifted_up_delay)
154 int time_current = SDL_GetTicks();
155 int pos = video.shifted_up_pos;
156 int pos_last = video.shifted_up_pos_last;
158 if (!DelayReachedExt(&video.shifted_up_delay, video.shifted_up_delay_value,
161 int delay = time_current - video.shifted_up_delay;
162 int delay_value = video.shifted_up_delay_value;
164 pos = pos_last + (pos - pos_last) * delay / delay_value;
168 video.shifted_up_pos_last = pos;
169 video.shifted_up_delay = 0;
172 SDL_Rect src_rect_up = { 0, pos, video.width, video.height - pos };
173 SDL_Rect dst_rect_up = { xoff, yoff, video.width, video.height - pos };
175 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
176 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
178 src_rect2 = &src_rect_up;
179 dst_rect2 = &dst_rect_up;
183 src_rect1 = &src_rect_up;
184 dst_rect1 = &dst_rect_up;
189 // clear render target buffer
190 SDL_RenderClear(sdl_renderer);
192 // set renderer to use target texture for rendering
193 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
194 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
195 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
197 // copy backbuffer texture to render target buffer
198 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
199 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
201 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
202 FinalizeScreen(DRAW_TO_SCREEN);
204 // when using target texture, copy it to screen buffer
205 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
206 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
208 SDL_SetRenderTarget(sdl_renderer, NULL);
209 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
212 #if defined(USE_TOUCH_INPUT_OVERLAY)
213 // draw overlay graphics for touch device input, if needed
214 DrawTouchInputOverlay();
217 // global synchronization point of the game to align video frame delay
218 if (with_frame_delay)
219 WaitUntilDelayReached(&video.frame_delay, video.frame_delay_value);
221 // show render target buffer on screen
222 SDL_RenderPresent(sdl_renderer);
225 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
227 UpdateScreenExt(rect, TRUE);
230 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
232 UpdateScreenExt(rect, FALSE);
235 void Delay_WithScreenUpdates(unsigned int delay)
237 unsigned int time_current = SDL_GetTicks();
238 unsigned int time_delayed = time_current + delay;
240 while (time_current < time_delayed)
242 // updating the screen contains waiting for frame delay (non-busy)
243 UpdateScreen_WithFrameDelay(NULL);
245 time_current = SDL_GetTicks();
249 static void SDLSetWindowIcon(char *basename)
251 // (setting the window icon on Mac OS X would replace the high-quality
252 // dock icon with the currently smaller (and uglier) icon from file)
254 #if !defined(PLATFORM_MACOSX)
255 char *filename = getCustomImageFilename(basename);
256 SDL_Surface *surface;
258 if (filename == NULL)
260 Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
265 if ((surface = IMG_Load(filename)) == NULL)
267 Error(ERR_WARN, "IMG_Load() failed: %s", SDL_GetError());
272 // set transparent color
273 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
274 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
276 SDL_SetWindowIcon(sdl_window, surface);
280 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
281 SDL_PixelFormat *format2)
283 return (format1->format == format2->format &&
284 format1->BitsPerPixel == format2->BitsPerPixel &&
285 format1->BytesPerPixel == format2->BytesPerPixel &&
286 format1->Rmask == format2->Rmask &&
287 format1->Gmask == format2->Gmask &&
288 format1->Bmask == format2->Bmask);
291 static Pixel SDLGetColorKey(SDL_Surface *surface)
295 if (SDL_GetColorKey(surface, &color_key) != 0)
301 static boolean SDLHasColorKey(SDL_Surface *surface)
303 return (SDLGetColorKey(surface) != -1);
306 static boolean SDLHasAlpha(SDL_Surface *surface)
308 SDL_BlendMode blend_mode;
310 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
313 return (blend_mode == SDL_BLENDMODE_BLEND);
316 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
318 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
320 SDL_SetSurfaceBlendMode(surface, blend_mode);
321 SDL_SetSurfaceAlphaMod(surface, alpha);
324 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
326 SDL_PixelFormat format;
327 SDL_Surface *new_surface;
332 if (backbuffer && backbuffer->surface)
334 format = *backbuffer->surface->format;
335 format.Amask = surface->format->Amask; // keep alpha channel
339 format = *surface->format;
342 new_surface = SDL_ConvertSurface(surface, &format, 0);
344 if (new_surface == NULL)
345 Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
350 boolean SDLSetNativeSurface(SDL_Surface **surface)
352 SDL_Surface *new_surface;
354 if (surface == NULL ||
356 backbuffer == NULL ||
357 backbuffer->surface == NULL)
360 // if pixel format already optimized for destination surface, do nothing
361 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
364 new_surface = SDLGetNativeSurface(*surface);
366 SDL_FreeSurface(*surface);
368 *surface = new_surface;
373 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
375 if (program.headless)
378 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
381 Error(ERR_EXIT, "SDL_CreateTextureFromSurface() failed: %s",
387 void SDLCreateBitmapTextures(Bitmap *bitmap)
393 SDL_DestroyTexture(bitmap->texture);
394 if (bitmap->texture_masked)
395 SDL_DestroyTexture(bitmap->texture_masked);
397 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
398 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
401 void SDLFreeBitmapTextures(Bitmap *bitmap)
407 SDL_DestroyTexture(bitmap->texture);
408 if (bitmap->texture_masked)
409 SDL_DestroyTexture(bitmap->texture_masked);
411 bitmap->texture = NULL;
412 bitmap->texture_masked = NULL;
415 void SDLInitVideoDisplay(void)
417 // initialize SDL video
418 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
419 Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
421 // set default SDL depth
422 video.default_depth = 32; // (how to determine video depth in SDL2?)
424 // Code used with SDL 1.2:
425 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
428 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
430 if (program.headless)
433 video.window_scaling_percent = setup.window_scaling_percent;
434 video.window_scaling_quality = setup.window_scaling_quality;
436 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
438 // SDL 2.0: support for (desktop) fullscreen mode available
439 video.fullscreen_available = TRUE;
441 // open SDL video output device (window or fullscreen mode)
442 if (!SDLSetVideoMode(fullscreen))
443 Error(ERR_EXIT, "setting video mode failed");
445 // !!! SDL2 can only set the window icon if the window already exists !!!
447 SDLSetWindowIcon(program.icon_filename);
449 // set window and icon title
453 static void SDLInitVideoBuffer_DrawBuffer(void)
455 /* SDL cannot directly draw to the visible video framebuffer like X11,
456 but always uses a backbuffer, which is then blitted to the visible
457 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
458 visible video framebuffer with 'SDL_Flip', if the hardware supports
459 this). Therefore do not use an additional backbuffer for drawing, but
460 use a symbolic buffer (distinguishable from the SDL backbuffer) called
461 'window', which indicates that the SDL backbuffer should be updated to
462 the visible video framebuffer when attempting to blit to it.
464 For convenience, it seems to be a good idea to create this symbolic
465 buffer 'window' at the same size as the SDL backbuffer. Although it
466 should never be drawn to directly, it would do no harm nevertheless. */
468 // create additional (symbolic) buffer for double-buffering
469 ReCreateBitmap(&window, video.width, video.height);
471 // create dummy drawing buffer for headless mode, if needed
472 if (program.headless)
473 ReCreateBitmap(&backbuffer, video.width, video.height);
476 void SDLInitVideoBuffer(boolean fullscreen)
478 SDLInitVideoBuffer_VideoBuffer(fullscreen);
479 SDLInitVideoBuffer_DrawBuffer();
482 static boolean SDLCreateScreen(boolean fullscreen)
484 SDL_Surface *new_surface = NULL;
486 int surface_flags_window = SURFACE_FLAGS | SDL_WINDOW_RESIZABLE;
487 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
490 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
492 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
493 _without_ enabling 2D/3D acceleration and/or guest additions installed,
494 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
495 it will try to use accelerated graphics and apparently fails miserably) */
496 int renderer_flags = SDL_RENDERER_SOFTWARE;
499 SDLSetScreenSizeAndOffsets(video.width, video.height);
501 int width = video.width;
502 int height = video.height;
503 int screen_width = video.screen_width;
504 int screen_height = video.screen_height;
505 int surface_flags = (fullscreen ? surface_flags_fullscreen :
506 surface_flags_window);
508 // default window size is unscaled
509 video.window_width = screen_width;
510 video.window_height = screen_height;
512 // store if initial screen mode is fullscreen mode when changing screen size
513 video.fullscreen_initial = fullscreen;
515 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
517 video.window_width = window_scaling_factor * screen_width;
518 video.window_height = window_scaling_factor * screen_height;
520 if (sdl_texture_stream)
522 SDL_DestroyTexture(sdl_texture_stream);
523 sdl_texture_stream = NULL;
526 if (sdl_texture_target)
528 SDL_DestroyTexture(sdl_texture_target);
529 sdl_texture_target = NULL;
532 if (!(fullscreen && fullscreen_enabled))
536 SDL_DestroyRenderer(sdl_renderer);
542 SDL_DestroyWindow(sdl_window);
547 if (sdl_window == NULL)
548 sdl_window = SDL_CreateWindow(program.window_title,
549 SDL_WINDOWPOS_CENTERED,
550 SDL_WINDOWPOS_CENTERED,
555 if (sdl_window != NULL)
557 if (sdl_renderer == NULL)
558 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
560 if (sdl_renderer != NULL)
562 SDL_RenderSetLogicalSize(sdl_renderer, screen_width, screen_height);
563 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
564 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
566 SDLSetScreenVsyncMode(setup.vsync_mode);
568 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
569 SDL_PIXELFORMAT_ARGB8888,
570 SDL_TEXTUREACCESS_STREAMING,
573 if (SDL_RenderTargetSupported(sdl_renderer))
574 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
575 SDL_PIXELFORMAT_ARGB8888,
576 SDL_TEXTUREACCESS_TARGET,
579 if (sdl_texture_stream != NULL)
581 // use SDL default values for RGB masks and no alpha channel
582 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
584 if (new_surface == NULL)
585 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
589 Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
594 Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
599 Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
602 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
603 if (new_surface != NULL)
604 fullscreen_enabled = fullscreen;
606 if (backbuffer == NULL)
607 backbuffer = CreateBitmapStruct();
609 backbuffer->width = video.width;
610 backbuffer->height = video.height;
612 if (backbuffer->surface)
613 SDL_FreeSurface(backbuffer->surface);
615 backbuffer->surface = new_surface;
617 return (new_surface != NULL);
620 boolean SDLSetVideoMode(boolean fullscreen)
622 boolean success = FALSE;
626 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
628 // switch display to fullscreen mode, if available
629 success = SDLCreateScreen(TRUE);
633 // switching display to fullscreen mode failed -- do not try it again
634 video.fullscreen_available = FALSE;
638 video.fullscreen_enabled = TRUE;
642 if ((!fullscreen && video.fullscreen_enabled) || !success)
644 // switch display to window mode
645 success = SDLCreateScreen(FALSE);
649 // switching display to window mode failed -- should not happen
653 video.fullscreen_enabled = FALSE;
654 video.window_scaling_percent = setup.window_scaling_percent;
655 video.window_scaling_quality = setup.window_scaling_quality;
657 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
661 SDLRedrawWindow(); // map window
666 void SDLSetWindowTitle(void)
668 if (sdl_window == NULL)
671 SDL_SetWindowTitle(sdl_window, program.window_title);
674 void SDLSetWindowScaling(int window_scaling_percent)
676 if (sdl_window == NULL)
679 float window_scaling_factor = (float)window_scaling_percent / 100;
680 int new_window_width = (int)(window_scaling_factor * video.screen_width);
681 int new_window_height = (int)(window_scaling_factor * video.screen_height);
683 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
685 video.window_scaling_percent = window_scaling_percent;
686 video.window_width = new_window_width;
687 video.window_height = new_window_height;
692 void SDLSetWindowScalingQuality(char *window_scaling_quality)
694 SDL_Texture *new_texture;
696 if (sdl_texture_stream == NULL)
699 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
701 new_texture = SDL_CreateTexture(sdl_renderer,
702 SDL_PIXELFORMAT_ARGB8888,
703 SDL_TEXTUREACCESS_STREAMING,
704 video.width, video.height);
706 if (new_texture != NULL)
708 SDL_DestroyTexture(sdl_texture_stream);
710 sdl_texture_stream = new_texture;
713 if (SDL_RenderTargetSupported(sdl_renderer))
714 new_texture = SDL_CreateTexture(sdl_renderer,
715 SDL_PIXELFORMAT_ARGB8888,
716 SDL_TEXTUREACCESS_TARGET,
717 video.width, video.height);
721 if (new_texture != NULL)
723 SDL_DestroyTexture(sdl_texture_target);
725 sdl_texture_target = new_texture;
730 video.window_scaling_quality = window_scaling_quality;
733 void SDLSetWindowFullscreen(boolean fullscreen)
735 if (sdl_window == NULL)
738 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
740 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
741 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
743 // if screen size was changed in fullscreen mode, correct desktop window size
744 if (!fullscreen && video.fullscreen_initial)
746 SDLSetWindowScaling(setup.window_scaling_percent);
747 SDL_SetWindowPosition(sdl_window,
748 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
750 video.fullscreen_initial = FALSE;
754 void SDLSetDisplaySize(void)
756 SDL_Rect display_bounds;
758 SDL_GetDisplayBounds(0, &display_bounds);
760 video.display_width = display_bounds.w;
761 video.display_height = display_bounds.h;
764 Error(ERR_DEBUG, "SDL real screen size: %d x %d",
765 video.display_width, video.display_height);
769 void SDLSetScreenSizeAndOffsets(int width, int height)
771 // set default video screen size and offsets
772 video.screen_width = width;
773 video.screen_height = height;
774 video.screen_xoffset = 0;
775 video.screen_yoffset = 0;
777 #if defined(USE_COMPLETE_DISPLAY)
778 float ratio_video = (float) width / height;
779 float ratio_display = (float) video.display_width / video.display_height;
781 if (ratio_video != ratio_display)
783 // adjust drawable screen size to cover the whole device display
785 if (ratio_video < ratio_display)
786 video.screen_width *= ratio_display / ratio_video;
788 video.screen_height *= ratio_video / ratio_display;
790 video.screen_xoffset = (video.screen_width - width) / 2;
791 video.screen_yoffset = (video.screen_height - height) / 2;
794 Error(ERR_DEBUG, "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
796 video.screen_width, video.screen_height,
797 ratio_video, ratio_display);
803 void SDLSetScreenSizeForRenderer(int width, int height)
805 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
808 void SDLSetScreenProperties(void)
810 SDLSetScreenSizeAndOffsets(video.width, video.height);
811 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
814 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
816 video.screen_rendering_mode =
817 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
818 SPECIAL_RENDERING_BITMAP :
819 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
820 SPECIAL_RENDERING_TARGET:
821 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
822 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
825 void SDLSetScreenVsyncMode(char *vsync_mode)
828 (strEqual(vsync_mode, STR_VSYNC_MODE_NORMAL) ? VSYNC_MODE_NORMAL :
829 strEqual(vsync_mode, STR_VSYNC_MODE_ADAPTIVE) ? VSYNC_MODE_ADAPTIVE :
831 int result = SDL_GL_SetSwapInterval(interval);
833 // if adaptive vsync requested, but not supported, retry with normal vsync
834 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
835 SDL_GL_SetSwapInterval(VSYNC_MODE_NORMAL);
838 void SDLRedrawWindow(void)
840 UpdateScreen_WithoutFrameDelay(NULL);
843 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
846 if (program.headless)
849 SDL_Surface *surface =
850 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
853 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
855 SDLSetNativeSurface(&surface);
857 bitmap->surface = surface;
860 void SDLFreeBitmapPointers(Bitmap *bitmap)
863 SDL_FreeSurface(bitmap->surface);
864 if (bitmap->surface_masked)
865 SDL_FreeSurface(bitmap->surface_masked);
867 bitmap->surface = NULL;
868 bitmap->surface_masked = NULL;
871 SDL_DestroyTexture(bitmap->texture);
872 if (bitmap->texture_masked)
873 SDL_DestroyTexture(bitmap->texture_masked);
875 bitmap->texture = NULL;
876 bitmap->texture_masked = NULL;
879 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
880 int src_x, int src_y, int width, int height,
881 int dst_x, int dst_y, int mask_mode)
883 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
884 SDL_Rect src_rect, dst_rect;
896 // if (src_bitmap != backbuffer || dst_bitmap != window)
897 if (!(src_bitmap == backbuffer && dst_bitmap == window))
898 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
899 src_bitmap->surface_masked : src_bitmap->surface),
900 &src_rect, real_dst_bitmap->surface, &dst_rect);
902 if (dst_bitmap == window)
903 UpdateScreen_WithFrameDelay(&dst_rect);
906 void SDLBlitTexture(Bitmap *bitmap,
907 int src_x, int src_y, int width, int height,
908 int dst_x, int dst_y, int mask_mode)
910 SDL_Texture *texture;
915 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
930 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
933 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
936 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
944 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
946 if (dst_bitmap == window)
947 UpdateScreen_WithFrameDelay(&rect);
950 void PrepareFadeBitmap(int draw_target)
952 Bitmap *fade_bitmap =
953 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
954 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
956 if (fade_bitmap == NULL)
959 // copy backbuffer to fading buffer
960 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
962 // add border and animations to fading buffer
963 FinalizeScreen(draw_target);
966 void SDLFadeRectangle(int x, int y, int width, int height,
967 int fade_mode, int fade_delay, int post_delay,
968 void (*draw_border_function)(void))
970 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
971 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
972 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
973 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
974 SDL_Surface *surface_screen = backbuffer->surface;
975 SDL_Rect src_rect, dst_rect;
977 int src_x = x, src_y = y;
978 int dst_x = x, dst_y = y;
979 unsigned int time_last, time_current;
981 // store function for drawing global masked border
982 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
984 // deactivate drawing of global border while fading, if needed
985 if (draw_border_function == NULL)
986 gfx.draw_global_border_function = NULL;
995 dst_rect.w = width; // (ignored)
996 dst_rect.h = height; // (ignored)
998 dst_rect2 = dst_rect;
1000 // before fading in, store backbuffer (without animation graphics)
1001 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1002 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1004 // copy source and target surfaces to temporary surfaces for fading
1005 if (fade_mode & FADE_TYPE_TRANSFORM)
1007 // (source and target fading buffer already prepared)
1009 else if (fade_mode & FADE_TYPE_FADE_IN)
1011 // (target fading buffer already prepared)
1012 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1014 else // FADE_TYPE_FADE_OUT
1016 // (source fading buffer already prepared)
1017 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1020 time_current = SDL_GetTicks();
1022 if (fade_mode == FADE_MODE_MELT)
1024 boolean done = FALSE;
1025 int melt_pixels = 2;
1026 int melt_columns = width / melt_pixels;
1027 int ypos[melt_columns];
1028 int max_steps = height / 8 + 32;
1033 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1035 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1037 ypos[0] = -GetSimpleRandom(16);
1039 for (i = 1 ; i < melt_columns; i++)
1041 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1043 ypos[i] = ypos[i - 1] + r;
1056 time_last = time_current;
1057 time_current = SDL_GetTicks();
1058 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1059 steps_final = MIN(MAX(0, steps), max_steps);
1063 done = (steps_done >= steps_final);
1065 for (i = 0 ; i < melt_columns; i++)
1073 else if (ypos[i] < height)
1078 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1080 if (ypos[i] + dy >= height)
1081 dy = height - ypos[i];
1083 // copy part of (appearing) target surface to upper area
1084 src_rect.x = src_x + i * melt_pixels;
1085 // src_rect.y = src_y + ypos[i];
1087 src_rect.w = melt_pixels;
1089 src_rect.h = ypos[i] + dy;
1091 dst_rect.x = dst_x + i * melt_pixels;
1092 // dst_rect.y = dst_y + ypos[i];
1095 if (steps_done >= steps_final)
1096 SDL_BlitSurface(surface_target, &src_rect,
1097 surface_screen, &dst_rect);
1101 // copy part of (disappearing) source surface to lower area
1102 src_rect.x = src_x + i * melt_pixels;
1104 src_rect.w = melt_pixels;
1105 src_rect.h = height - ypos[i];
1107 dst_rect.x = dst_x + i * melt_pixels;
1108 dst_rect.y = dst_y + ypos[i];
1110 if (steps_done >= steps_final)
1111 SDL_BlitSurface(surface_source, &src_rect,
1112 surface_screen, &dst_rect);
1118 src_rect.x = src_x + i * melt_pixels;
1120 src_rect.w = melt_pixels;
1121 src_rect.h = height;
1123 dst_rect.x = dst_x + i * melt_pixels;
1126 if (steps_done >= steps_final)
1127 SDL_BlitSurface(surface_target, &src_rect,
1128 surface_screen, &dst_rect);
1132 if (steps_done >= steps_final)
1134 if (draw_border_function != NULL)
1135 draw_border_function();
1137 UpdateScreen_WithFrameDelay(&dst_rect2);
1141 else if (fade_mode == FADE_MODE_CURTAIN)
1145 int xx_size = width / 2;
1147 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1149 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1151 for (xx = 0; xx < xx_size;)
1153 time_last = time_current;
1154 time_current = SDL_GetTicks();
1155 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1156 xx_final = MIN(MAX(0, xx), xx_size);
1161 src_rect.h = height;
1166 // draw new (target) image to screen buffer
1167 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1169 if (xx_final < xx_size)
1171 src_rect.w = xx_size - xx_final;
1172 src_rect.h = height;
1174 // draw old (source) image to screen buffer (left side)
1176 src_rect.x = src_x + xx_final;
1179 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1181 // draw old (source) image to screen buffer (right side)
1183 src_rect.x = src_x + xx_size;
1184 dst_rect.x = dst_x + xx_size + xx_final;
1186 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1189 if (draw_border_function != NULL)
1190 draw_border_function();
1192 // only update the region of the screen that is affected from fading
1193 UpdateScreen_WithFrameDelay(&dst_rect2);
1196 else // fading in, fading out or cross-fading
1201 for (alpha = 0.0; alpha < 255.0;)
1203 time_last = time_current;
1204 time_current = SDL_GetTicks();
1205 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1206 alpha_final = MIN(MAX(0, alpha), 255);
1208 // draw existing (source) image to screen buffer
1209 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1211 // draw new (target) image to screen buffer using alpha blending
1212 SDLSetAlpha(surface_target, TRUE, alpha_final);
1213 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1215 if (draw_border_function != NULL)
1216 draw_border_function();
1218 // only update the region of the screen that is affected from fading
1219 UpdateScreen_WithFrameDelay(&dst_rect);
1224 Delay_WithScreenUpdates(post_delay);
1226 // restore function for drawing global masked border
1227 gfx.draw_global_border_function = draw_global_border_function;
1229 // after fading in, restore backbuffer (without animation graphics)
1230 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1231 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1234 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1235 int to_x, int to_y, Uint32 color)
1237 SDL_Surface *surface = dst_bitmap->surface;
1241 swap_numbers(&from_x, &to_x);
1244 swap_numbers(&from_y, &to_y);
1248 rect.w = (to_x - from_x + 1);
1249 rect.h = (to_y - from_y + 1);
1251 SDL_FillRect(surface, &rect, color);
1254 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1255 int to_x, int to_y, Uint32 color)
1257 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1260 #if ENABLE_UNUSED_CODE
1261 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1262 int num_points, Uint32 color)
1267 for (i = 0; i < num_points - 1; i++)
1269 for (x = 0; x < line_width; x++)
1271 for (y = 0; y < line_width; y++)
1273 int dx = x - line_width / 2;
1274 int dy = y - line_width / 2;
1276 if ((x == 0 && y == 0) ||
1277 (x == 0 && y == line_width - 1) ||
1278 (x == line_width - 1 && y == 0) ||
1279 (x == line_width - 1 && y == line_width - 1))
1282 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1283 points[i+1].x + dx, points[i+1].y + dy, color);
1290 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1292 SDL_Surface *surface = src_bitmap->surface;
1294 switch (surface->format->BytesPerPixel)
1296 case 1: // assuming 8-bpp
1298 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1302 case 2: // probably 15-bpp or 16-bpp
1304 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1308 case 3: // slow 24-bpp mode; usually not used
1311 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1315 shift = surface->format->Rshift;
1316 color |= *(pix + shift / 8) >> shift;
1317 shift = surface->format->Gshift;
1318 color |= *(pix + shift / 8) >> shift;
1319 shift = surface->format->Bshift;
1320 color |= *(pix + shift / 8) >> shift;
1326 case 4: // probably 32-bpp
1328 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1337 // ============================================================================
1338 // The following functions were taken from the SGE library
1339 // (SDL Graphics Extension Library) by Anders Lindström
1340 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1341 // ============================================================================
1343 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1345 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1347 switch (surface->format->BytesPerPixel)
1352 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1358 // Probably 15-bpp or 16-bpp
1359 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1365 // Slow 24-bpp mode, usually not used
1369 // Gack - slow, but endian correct
1370 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1371 shift = surface->format->Rshift;
1372 *(pix+shift/8) = color>>shift;
1373 shift = surface->format->Gshift;
1374 *(pix+shift/8) = color>>shift;
1375 shift = surface->format->Bshift;
1376 *(pix+shift/8) = color>>shift;
1383 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1391 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1392 Uint8 R, Uint8 G, Uint8 B)
1394 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1397 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1399 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1402 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1404 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1407 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1412 // Gack - slow, but endian correct
1413 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1414 shift = surface->format->Rshift;
1415 *(pix+shift/8) = color>>shift;
1416 shift = surface->format->Gshift;
1417 *(pix+shift/8) = color>>shift;
1418 shift = surface->format->Bshift;
1419 *(pix+shift/8) = color>>shift;
1422 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1424 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1427 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1429 switch (dest->format->BytesPerPixel)
1432 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1436 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1440 _PutPixel24(dest,x,y,color);
1444 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1450 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1452 if (SDL_MUSTLOCK(surface))
1454 if (SDL_LockSurface(surface) < 0)
1460 _PutPixel(surface, x, y, color);
1462 if (SDL_MUSTLOCK(surface))
1464 SDL_UnlockSurface(surface);
1469 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1470 Uint8 r, Uint8 g, Uint8 b)
1472 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1475 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1477 if (y >= 0 && y <= dest->h - 1)
1479 switch (dest->format->BytesPerPixel)
1482 return y*dest->pitch;
1486 return y*dest->pitch/2;
1490 return y*dest->pitch;
1494 return y*dest->pitch/4;
1502 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1505 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1507 switch (surface->format->BytesPerPixel)
1512 *((Uint8 *)surface->pixels + ypitch + x) = color;
1518 // Probably 15-bpp or 16-bpp
1519 *((Uint16 *)surface->pixels + ypitch + x) = color;
1525 // Slow 24-bpp mode, usually not used
1529 // Gack - slow, but endian correct
1530 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1531 shift = surface->format->Rshift;
1532 *(pix+shift/8) = color>>shift;
1533 shift = surface->format->Gshift;
1534 *(pix+shift/8) = color>>shift;
1535 shift = surface->format->Bshift;
1536 *(pix+shift/8) = color>>shift;
1543 *((Uint32 *)surface->pixels + ypitch + x) = color;
1550 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1555 if (SDL_MUSTLOCK(Surface))
1557 if (SDL_LockSurface(Surface) < 0)
1571 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1575 if (x2 > Surface->w - 1)
1576 x2 = Surface->w - 1;
1583 SDL_FillRect(Surface, &l, Color);
1585 if (SDL_MUSTLOCK(Surface))
1587 SDL_UnlockSurface(Surface);
1591 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1592 Uint8 R, Uint8 G, Uint8 B)
1594 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1597 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1610 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1614 if (x2 > Surface->w - 1)
1615 x2 = Surface->w - 1;
1622 SDL_FillRect(Surface, &l, Color);
1625 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1630 if (SDL_MUSTLOCK(Surface))
1632 if (SDL_LockSurface(Surface) < 0)
1646 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1650 if (y2 > Surface->h - 1)
1651 y2 = Surface->h - 1;
1658 SDL_FillRect(Surface, &l, Color);
1660 if (SDL_MUSTLOCK(Surface))
1662 SDL_UnlockSurface(Surface);
1666 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1667 Uint8 R, Uint8 G, Uint8 B)
1669 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1672 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1685 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1689 if (y2 > Surface->h - 1)
1690 y2 = Surface->h - 1;
1697 SDL_FillRect(Surface, &l, Color);
1701 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1702 Sint16 x2, Sint16 y2, Uint32 Color,
1703 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1706 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1711 sdx = (dx < 0) ? -1 : 1;
1712 sdy = (dy < 0) ? -1 : 1;
1724 for (x = 0; x < dx; x++)
1726 Callback(Surface, px, py, Color);
1740 for (y = 0; y < dy; y++)
1742 Callback(Surface, px, py, Color);
1757 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1758 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1759 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1762 sge_DoLine(Surface, X1, Y1, X2, Y2,
1763 SDL_MapRGB(Surface->format, R, G, B), Callback);
1767 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1770 if (SDL_MUSTLOCK(Surface))
1772 if (SDL_LockSurface(Surface) < 0)
1777 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1779 // unlock the display
1780 if (SDL_MUSTLOCK(Surface))
1782 SDL_UnlockSurface(Surface);
1787 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1788 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1790 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1794 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1796 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1800 // ----------------------------------------------------------------------------
1801 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1802 // ----------------------------------------------------------------------------
1804 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1805 int width, int height, Uint32 color)
1809 for (y = src_y; y < src_y + height; y++)
1811 for (x = src_x; x < src_x + width; x++)
1813 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1815 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1820 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1821 int src_x, int src_y, int width, int height,
1822 int dst_x, int dst_y)
1826 for (y = 0; y < height; y++)
1828 for (x = 0; x < width; x++)
1830 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1832 if (pixel != BLACK_PIXEL)
1833 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1839 // ============================================================================
1840 // The following functions were taken from the SDL_gfx library version 2.0.3
1841 // (Rotozoomer) by Andreas Schiffler
1842 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1843 // ============================================================================
1845 // ----------------------------------------------------------------------------
1848 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1849 // ----------------------------------------------------------------------------
1859 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1862 tColorRGBA *sp, *csp, *dp;
1866 sp = csp = (tColorRGBA *) src->pixels;
1867 dp = (tColorRGBA *) dst->pixels;
1868 dgap = dst->pitch - dst->w * 4;
1870 for (y = 0; y < dst->h; y++)
1874 for (x = 0; x < dst->w; x++)
1876 tColorRGBA *sp0 = sp;
1877 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1878 tColorRGBA *sp00 = &sp0[0];
1879 tColorRGBA *sp01 = &sp0[1];
1880 tColorRGBA *sp10 = &sp1[0];
1881 tColorRGBA *sp11 = &sp1[1];
1884 // create new color pixel from all four source color pixels
1885 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1886 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1887 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1888 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1893 // advance source pointers
1896 // advance destination pointer
1900 // advance source pointer
1901 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1903 // advance destination pointers
1904 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1910 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1912 int x, y, *sax, *say, *csax, *csay;
1914 tColorRGBA *sp, *csp, *csp0, *dp;
1917 // use specialized zoom function when scaling down to exactly half size
1918 if (src->w == 2 * dst->w &&
1919 src->h == 2 * dst->h)
1920 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1923 sx = (float) src->w / (float) dst->w;
1924 sy = (float) src->h / (float) dst->h;
1926 // allocate memory for row increments
1927 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1928 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1930 // precalculate row increments
1931 for (x = 0; x <= dst->w; x++)
1932 *csax++ = (int)(sx * x);
1934 for (y = 0; y <= dst->h; y++)
1935 *csay++ = (int)(sy * y);
1938 sp = csp = csp0 = (tColorRGBA *) src->pixels;
1939 dp = (tColorRGBA *) dst->pixels;
1940 dgap = dst->pitch - dst->w * 4;
1943 for (y = 0; y < dst->h; y++)
1948 for (x = 0; x < dst->w; x++)
1953 // advance source pointers
1957 // advance destination pointer
1961 // advance source pointer
1963 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
1965 // advance destination pointers
1966 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1975 // ----------------------------------------------------------------------------
1978 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
1979 // ----------------------------------------------------------------------------
1981 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
1983 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1984 Uint8 *sp, *dp, *csp;
1988 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
1989 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
1991 // allocate memory for row increments
1992 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
1993 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
1995 // precalculate row increments
1998 for (x = 0; x < dst->w; x++)
2001 *csax = (csx >> 16);
2008 for (y = 0; y < dst->h; y++)
2011 *csay = (csy >> 16);
2018 for (x = 0; x < dst->w; x++)
2026 for (y = 0; y < dst->h; y++)
2033 sp = csp = (Uint8 *) src->pixels;
2034 dp = (Uint8 *) dst->pixels;
2035 dgap = dst->pitch - dst->w;
2039 for (y = 0; y < dst->h; y++)
2043 for (x = 0; x < dst->w; x++)
2048 // advance source pointers
2052 // advance destination pointer
2056 // advance source pointer (for row)
2057 csp += ((*csay) * src->pitch);
2060 // advance destination pointers
2070 // ----------------------------------------------------------------------------
2073 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2074 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2075 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2076 // into a 32bit RGBA format on the fly.
2077 // ----------------------------------------------------------------------------
2079 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2081 SDL_Surface *zoom_src = NULL;
2082 SDL_Surface *zoom_dst = NULL;
2083 boolean is_converted = FALSE;
2090 // determine if source surface is 32 bit or 8 bit
2091 is_32bit = (src->format->BitsPerPixel == 32);
2093 if (is_32bit || src->format->BitsPerPixel == 8)
2095 // use source surface 'as is'
2100 // new source surface is 32 bit with a defined RGB ordering
2101 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2102 0x000000ff, 0x0000ff00, 0x00ff0000,
2103 (src->format->Amask ? 0xff000000 : 0));
2104 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2106 is_converted = TRUE;
2109 // allocate surface to completely contain the zoomed surface
2112 // target surface is 32 bit with source RGBA/ABGR ordering
2113 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2114 zoom_src->format->Rmask,
2115 zoom_src->format->Gmask,
2116 zoom_src->format->Bmask,
2117 zoom_src->format->Amask);
2121 // target surface is 8 bit
2122 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2126 // lock source surface
2127 SDL_LockSurface(zoom_src);
2129 // check which kind of surface we have
2132 // call the 32 bit transformation routine to do the zooming
2133 zoomSurfaceRGBA(zoom_src, zoom_dst);
2138 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2139 zoom_dst->format->palette->colors[i] =
2140 zoom_src->format->palette->colors[i];
2141 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2143 // call the 8 bit transformation routine to do the zooming
2144 zoomSurfaceY(zoom_src, zoom_dst);
2147 // unlock source surface
2148 SDL_UnlockSurface(zoom_src);
2150 // free temporary surface
2152 SDL_FreeSurface(zoom_src);
2154 // return destination surface
2158 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2160 SDL_Surface *new_surface;
2162 if (surface == NULL)
2165 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2166 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2168 // remove alpha channel from native non-transparent surface, if defined
2169 SDLSetAlpha(new_surface, FALSE, 0);
2171 // remove transparent color from native non-transparent surface, if defined
2172 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2177 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2179 Bitmap *dst_bitmap = CreateBitmapStruct();
2180 SDL_Surface *src_surface = src_bitmap->surface_masked;
2181 SDL_Surface *dst_surface;
2183 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2184 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2186 dst_bitmap->width = dst_width;
2187 dst_bitmap->height = dst_height;
2189 // create zoomed temporary surface from source surface
2190 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2192 // create native format destination surface from zoomed temporary surface
2193 SDLSetNativeSurface(&dst_surface);
2195 // set color key for zoomed surface from source surface, if defined
2196 if (SDLHasColorKey(src_surface))
2197 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2198 SDLGetColorKey(src_surface));
2200 // create native non-transparent surface for opaque blitting
2201 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2203 // set native transparent surface for masked blitting
2204 dst_bitmap->surface_masked = dst_surface;
2210 // ============================================================================
2211 // load image to bitmap
2212 // ============================================================================
2214 Bitmap *SDLLoadImage(char *filename)
2216 Bitmap *new_bitmap = CreateBitmapStruct();
2217 SDL_Surface *sdl_image_tmp;
2219 if (program.headless)
2221 // prevent sanity check warnings at later stage
2222 new_bitmap->width = new_bitmap->height = 1;
2227 print_timestamp_init("SDLLoadImage");
2229 print_timestamp_time(getBaseNamePtr(filename));
2231 // load image to temporary surface
2232 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2233 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
2235 print_timestamp_time("IMG_Load");
2237 UPDATE_BUSY_STATE();
2239 // create native non-transparent surface for current image
2240 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2241 Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2243 print_timestamp_time("SDLGetNativeSurface (opaque)");
2245 UPDATE_BUSY_STATE();
2247 // set black pixel to transparent if no alpha channel / transparent color
2248 if (!SDLHasAlpha(sdl_image_tmp) &&
2249 !SDLHasColorKey(sdl_image_tmp))
2250 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2251 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2253 // create native transparent surface for current image
2254 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2255 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2257 print_timestamp_time("SDLGetNativeSurface (masked)");
2259 UPDATE_BUSY_STATE();
2261 // free temporary surface
2262 SDL_FreeSurface(sdl_image_tmp);
2264 new_bitmap->width = new_bitmap->surface->w;
2265 new_bitmap->height = new_bitmap->surface->h;
2267 print_timestamp_done("SDLLoadImage");
2273 // ----------------------------------------------------------------------------
2274 // custom cursor fuctions
2275 // ----------------------------------------------------------------------------
2277 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2279 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2280 cursor_info->width, cursor_info->height,
2281 cursor_info->hot_x, cursor_info->hot_y);
2284 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2286 static struct MouseCursorInfo *last_cursor_info = NULL;
2287 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2288 static SDL_Cursor *cursor_default = NULL;
2289 static SDL_Cursor *cursor_current = NULL;
2291 // if invoked for the first time, store the SDL default cursor
2292 if (cursor_default == NULL)
2293 cursor_default = SDL_GetCursor();
2295 // only create new cursor if cursor info (custom only) has changed
2296 if (cursor_info != NULL && cursor_info != last_cursor_info)
2298 cursor_current = create_cursor(cursor_info);
2299 last_cursor_info = cursor_info;
2302 // only set new cursor if cursor info (custom or NULL) has changed
2303 if (cursor_info != last_cursor_info2)
2304 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2306 last_cursor_info2 = cursor_info;
2310 // ============================================================================
2312 // ============================================================================
2314 void SDLOpenAudio(void)
2316 if (program.headless)
2319 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2321 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2325 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2326 AUDIO_NUM_CHANNELS_STEREO,
2327 setup.system.audio_fragment_size) < 0)
2329 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2333 audio.sound_available = TRUE;
2334 audio.music_available = TRUE;
2335 audio.loops_available = TRUE;
2336 audio.sound_enabled = TRUE;
2338 // set number of available mixer channels
2339 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2340 audio.music_channel = MUSIC_CHANNEL;
2341 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2343 Mixer_InitChannels();
2346 void SDLCloseAudio(void)
2349 Mix_HaltChannel(-1);
2352 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2356 // ============================================================================
2358 // ============================================================================
2360 void SDLWaitEvent(Event *event)
2362 SDL_WaitEvent(event);
2366 // ============================================================================
2367 // joystick functions
2368 // ============================================================================
2370 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2371 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2372 static int sdl_js_axis[MAX_PLAYERS][2];
2373 static int sdl_js_button[MAX_PLAYERS][2];
2374 static boolean sdl_is_controller[MAX_PLAYERS];
2376 void SDLClearJoystickState(void)
2380 for (i = 0; i < MAX_PLAYERS; i++)
2382 for (j = 0; j < 2; j++)
2384 sdl_js_axis_raw[i][j] = -1;
2385 sdl_js_axis[i][j] = 0;
2386 sdl_js_button[i][j] = 0;
2391 boolean SDLOpenJoystick(int nr)
2393 if (nr < 0 || nr >= MAX_PLAYERS)
2396 sdl_is_controller[nr] = SDL_IsGameController(nr);
2399 Error(ERR_DEBUG, "opening joystick %d (%s)",
2400 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2403 if (sdl_is_controller[nr])
2404 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2406 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2408 return (sdl_joystick[nr] != NULL);
2411 void SDLCloseJoystick(int nr)
2413 if (nr < 0 || nr >= MAX_PLAYERS)
2417 Error(ERR_DEBUG, "closing joystick %d", nr);
2420 if (sdl_is_controller[nr])
2421 SDL_GameControllerClose(sdl_joystick[nr]);
2423 SDL_JoystickClose(sdl_joystick[nr]);
2425 sdl_joystick[nr] = NULL;
2428 boolean SDLCheckJoystickOpened(int nr)
2430 if (nr < 0 || nr >= MAX_PLAYERS)
2433 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2436 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2438 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2439 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2440 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2441 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2443 if (nr < 0 || nr >= MAX_PLAYERS)
2449 // prevent (slightly jittering, but centered) axis A from resetting axis B
2450 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2451 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2454 sdl_js_axis[nr][axis_id] = axis_value;
2455 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2458 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2460 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2461 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2462 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2463 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2464 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2465 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2466 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2467 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2470 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2471 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2472 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2473 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2474 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2475 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2476 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2477 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2479 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2480 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2481 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2482 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2483 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2485 if (nr < 0 || nr >= MAX_PLAYERS)
2488 if (button_id == -1)
2491 sdl_js_button[nr][button_id] = button_state;
2494 void HandleJoystickEvent(Event *event)
2498 case SDL_CONTROLLERDEVICEADDED:
2500 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2501 event->cdevice.which);
2506 case SDL_CONTROLLERDEVICEREMOVED:
2508 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2509 event->cdevice.which);
2514 case SDL_CONTROLLERAXISMOTION:
2516 Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2517 event->caxis.which, event->caxis.axis, event->caxis.value);
2519 setJoystickAxis(event->caxis.which,
2521 event->caxis.value);
2524 case SDL_CONTROLLERBUTTONDOWN:
2526 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2527 event->cbutton.which, event->cbutton.button);
2529 setJoystickButton(event->cbutton.which,
2530 event->cbutton.button,
2534 case SDL_CONTROLLERBUTTONUP:
2536 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2537 event->cbutton.which, event->cbutton.button);
2539 setJoystickButton(event->cbutton.which,
2540 event->cbutton.button,
2544 case SDL_JOYAXISMOTION:
2545 if (sdl_is_controller[event->jaxis.which])
2549 Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2550 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2552 if (event->jaxis.axis < 4)
2553 setJoystickAxis(event->jaxis.which,
2555 event->jaxis.value);
2558 case SDL_JOYBUTTONDOWN:
2559 if (sdl_is_controller[event->jaxis.which])
2563 Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2564 event->jbutton.which, event->jbutton.button);
2566 if (event->jbutton.button < 4)
2567 setJoystickButton(event->jbutton.which,
2568 event->jbutton.button,
2572 case SDL_JOYBUTTONUP:
2573 if (sdl_is_controller[event->jaxis.which])
2577 Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2578 event->jbutton.which, event->jbutton.button);
2580 if (event->jbutton.button < 4)
2581 setJoystickButton(event->jbutton.which,
2582 event->jbutton.button,
2591 void SDLInitJoysticks(void)
2593 static boolean sdl_joystick_subsystem_initialized = FALSE;
2594 boolean print_warning = !sdl_joystick_subsystem_initialized;
2595 char *mappings_file_base = getPath2(options.conf_directory,
2596 GAMECONTROLLER_BASENAME);
2597 char *mappings_file_user = getPath2(getUserGameDataDir(),
2598 GAMECONTROLLER_BASENAME);
2602 if (!sdl_joystick_subsystem_initialized)
2604 sdl_joystick_subsystem_initialized = TRUE;
2606 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2608 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2610 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2614 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2616 // the included game controller base mappings should always be found
2617 if (num_mappings == -1)
2618 Error(ERR_WARN, "no game controller base mappings found");
2621 Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2624 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2627 // the personal game controller user mappings may or may not be found
2628 if (num_mappings == -1)
2629 Error(ERR_WARN, "no game controller user mappings found");
2631 Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2633 Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2636 checked_free(mappings_file_base);
2637 checked_free(mappings_file_user);
2640 for (i = 0; i < SDL_NumJoysticks(); i++)
2642 const char *name, *type;
2644 if (SDL_IsGameController(i))
2646 name = SDL_GameControllerNameForIndex(i);
2647 type = "game controller";
2651 name = SDL_JoystickNameForIndex(i);
2655 Error(ERR_INFO, "- joystick %d (%s): '%s'",
2656 i, type, (name ? name : "(Unknown)"));
2661 // assign joysticks from configured to connected joystick for all players
2662 for (i = 0; i < MAX_PLAYERS; i++)
2664 // get configured joystick for this player
2665 char *device_name = setup.input[i].joy.device_name;
2666 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2668 if (joystick_nr >= SDL_NumJoysticks())
2670 if (setup.input[i].use_joystick && print_warning)
2671 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2676 // store configured joystick number for each player
2677 joystick.nr[i] = joystick_nr;
2680 // now open all connected joysticks (regardless if configured or not)
2681 for (i = 0; i < SDL_NumJoysticks(); i++)
2683 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2684 if (SDLCheckJoystickOpened(i))
2685 SDLCloseJoystick(i);
2687 if (SDLOpenJoystick(i))
2688 joystick.status = JOYSTICK_ACTIVATED;
2689 else if (print_warning)
2690 Error(ERR_WARN, "cannot open joystick %d", i);
2693 SDLClearJoystickState();
2696 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2698 if (nr < 0 || nr >= MAX_PLAYERS)
2702 *x = sdl_js_axis[nr][0];
2704 *y = sdl_js_axis[nr][1];
2707 *b1 = sdl_js_button[nr][0];
2709 *b2 = sdl_js_button[nr][1];
2715 // ============================================================================
2716 // touch input overlay functions
2717 // ============================================================================
2719 #if defined(USE_TOUCH_INPUT_OVERLAY)
2720 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2723 int grid_xsize = overlay.grid_xsize;
2724 int grid_ysize = overlay.grid_ysize;
2727 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2728 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2730 for (x = 0; x < grid_xsize; x++)
2732 rect.x = (x + 0) * video.screen_width / grid_xsize;
2733 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2735 for (y = 0; y < grid_ysize; y++)
2737 rect.y = (y + 0) * video.screen_height / grid_ysize;
2738 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2740 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2741 SDL_RenderDrawRect(sdl_renderer, &rect);
2745 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2748 static void RenderFillRectangle(int x, int y, int width, int height)
2750 SDL_Rect rect = { x, y, width, height };
2752 SDL_RenderFillRect(sdl_renderer, &rect);
2755 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2757 static int alpha_direction = 0;
2758 static int alpha_highlight = 0;
2759 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2760 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2762 int grid_xsize = overlay.grid_xsize;
2763 int grid_ysize = overlay.grid_ysize;
2766 if (alpha == alpha_max)
2768 if (alpha_direction < 0)
2770 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2772 if (alpha_highlight == 0)
2773 alpha_direction = 1;
2777 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2779 if (alpha_highlight == alpha_max)
2780 alpha_direction = -1;
2785 alpha_direction = 1;
2786 alpha_highlight = alpha;
2789 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2791 for (x = 0; x < grid_xsize; x++)
2793 for (y = 0; y < grid_ysize; y++)
2795 int grid_button = overlay.grid_button[x][y];
2796 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2797 int alpha_draw = alpha;
2798 int outline_border = MV_NONE;
2799 int border_size = 2;
2800 boolean draw_outlined = setup.touch.draw_outlined;
2801 boolean draw_pressed = setup.touch.draw_pressed;
2803 if (grid_button == CHAR_GRID_BUTTON_NONE)
2806 if (grid_button == overlay.grid_button_highlight)
2807 alpha_draw = alpha_highlight;
2809 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2812 draw_outlined = FALSE;
2814 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2817 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2819 rect.x = (x + 0) * video.screen_width / grid_xsize;
2820 rect.y = (y + 0) * video.screen_height / grid_ysize;
2821 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2822 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2824 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2826 rect.x += border_size;
2827 rect.w -= border_size;
2829 outline_border |= MV_LEFT;
2832 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2834 rect.w -= border_size;
2836 outline_border |= MV_RIGHT;
2839 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2841 rect.y += border_size;
2842 rect.h -= border_size;
2844 outline_border |= MV_UP;
2847 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2849 rect.h -= border_size;
2851 outline_border |= MV_DOWN;
2856 int rect_x = rect.x +
2857 (outline_border & MV_LEFT ? border_size : 0);
2858 int rect_w = rect.w -
2859 (outline_border & MV_LEFT ? border_size : 0) -
2860 (outline_border & MV_RIGHT ? border_size : 0);
2862 if (outline_border & MV_LEFT)
2863 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2865 if (outline_border & MV_RIGHT)
2866 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2867 border_size, rect.h);
2869 if (outline_border & MV_UP)
2870 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
2872 if (outline_border & MV_DOWN)
2873 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
2874 rect_w, border_size);
2878 SDL_RenderFillRect(sdl_renderer, &rect);
2883 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2886 static void DrawTouchInputOverlay(void)
2888 static boolean deactivated = TRUE;
2889 static boolean show_grid = FALSE;
2890 static int alpha = 0;
2891 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2892 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2893 boolean active = (overlay.enabled && overlay.active);
2895 if (!active && deactivated)
2900 if (alpha < alpha_max)
2901 alpha = MIN(alpha + alpha_step, alpha_max);
2903 deactivated = FALSE;
2907 alpha = MAX(0, alpha - alpha_step);
2913 if (overlay.show_grid)
2915 else if (deactivated)
2919 DrawTouchInputOverlay_ShowGrid(alpha);
2921 DrawTouchInputOverlay_ShowGridButtons(alpha);