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 #if defined(TARGET_SDL2)
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;
36 static boolean limit_screen_updates = FALSE;
39 /* functions from SGE library */
40 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
42 #if defined(USE_TOUCH_INPUT_OVERLAY)
43 /* functions to draw overlay graphics for touch device input */
44 static void DrawTouchInputOverlay();
47 void SDLLimitScreenUpdates(boolean enable)
49 limit_screen_updates = enable;
52 static void FinalizeScreen(int draw_target)
54 // copy global animations to render target buffer, if defined (below border)
55 if (gfx.draw_global_anim_function != NULL)
56 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
58 // copy global masked border to render target buffer, if defined
59 if (gfx.draw_global_border_function != NULL)
60 gfx.draw_global_border_function(draw_target);
62 // copy global animations to render target buffer, if defined (above border)
63 if (gfx.draw_global_anim_function != NULL)
64 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
66 // copy tile selection cursor to render target buffer, if defined (above all)
67 if (gfx.draw_tile_cursor_function != NULL)
68 gfx.draw_tile_cursor_function(draw_target);
71 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
73 static unsigned int update_screen_delay = 0;
74 unsigned int update_screen_delay_value = 50; /* (milliseconds) */
75 SDL_Surface *screen = backbuffer->surface;
77 if (limit_screen_updates &&
78 !DelayReached(&update_screen_delay, update_screen_delay_value))
81 LimitScreenUpdates(FALSE);
85 static int LastFrameCounter = 0;
86 boolean changed = (FrameCounter != LastFrameCounter);
88 printf("::: FrameCounter == %d [%s]\n", FrameCounter,
89 (changed ? "-" : "SAME FRAME UPDATED"));
91 LastFrameCounter = FrameCounter;
100 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
101 gfx.final_screen_bitmap != NULL) // may not be initialized yet
103 // draw global animations using bitmaps instead of using textures
104 // to prevent texture scaling artefacts (this is potentially slower)
106 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
107 gfx.win_xsize, gfx.win_ysize, 0, 0);
109 FinalizeScreen(DRAW_TO_SCREEN);
111 screen = gfx.final_screen_bitmap->surface;
113 // force full window redraw
117 #if defined(TARGET_SDL2)
118 SDL_Texture *sdl_texture = sdl_texture_stream;
120 // deactivate use of target texture if render targets are not supported
121 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
122 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
123 sdl_texture_target == NULL)
124 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
126 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
127 sdl_texture = sdl_texture_target;
131 int bytes_x = screen->pitch / video.width;
132 int bytes_y = screen->pitch;
134 SDL_UpdateTexture(sdl_texture, rect,
135 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
140 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
143 int xoff = video.screen_xoffset;
144 int yoff = video.screen_yoffset;
145 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
146 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
147 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
149 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
150 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
151 dst_rect2 = &dst_rect_screen;
153 dst_rect1 = &dst_rect_screen;
155 #if defined(HAS_SCREEN_KEYBOARD)
156 if (video.shifted_up || video.shifted_up_delay)
158 int time_current = SDL_GetTicks();
159 int pos = video.shifted_up_pos;
160 int pos_last = video.shifted_up_pos_last;
162 if (!DelayReachedExt(&video.shifted_up_delay, video.shifted_up_delay_value,
165 int delay = time_current - video.shifted_up_delay;
166 int delay_value = video.shifted_up_delay_value;
168 pos = pos_last + (pos - pos_last) * delay / delay_value;
172 video.shifted_up_pos_last = pos;
173 video.shifted_up_delay = 0;
176 SDL_Rect src_rect_up = { 0, pos, video.width, video.height - pos };
177 SDL_Rect dst_rect_up = { xoff, yoff, video.width, video.height - pos };
179 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
180 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
182 src_rect2 = &src_rect_up;
183 dst_rect2 = &dst_rect_up;
187 src_rect1 = &src_rect_up;
188 dst_rect1 = &dst_rect_up;
193 // clear render target buffer
194 SDL_RenderClear(sdl_renderer);
196 // set renderer to use target texture for rendering
197 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
198 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
199 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
201 // copy backbuffer texture to render target buffer
202 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
203 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
205 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
206 FinalizeScreen(DRAW_TO_SCREEN);
208 // when using target texture, copy it to screen buffer
209 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
210 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
212 SDL_SetRenderTarget(sdl_renderer, NULL);
213 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
216 #if defined(USE_TOUCH_INPUT_OVERLAY)
217 // draw overlay graphics for touch device input, if needed
218 DrawTouchInputOverlay();
223 // global synchronization point of the game to align video frame delay
224 if (with_frame_delay)
225 WaitUntilDelayReached(&video.frame_delay, video.frame_delay_value);
227 #if defined(TARGET_SDL2)
228 // show render target buffer on screen
229 SDL_RenderPresent(sdl_renderer);
232 SDL_UpdateRects(screen, 1, rect);
234 SDL_UpdateRect(screen, 0, 0, 0, 0);
238 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
240 UpdateScreenExt(rect, TRUE);
243 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
245 UpdateScreenExt(rect, FALSE);
248 void Delay_WithScreenUpdates(unsigned int delay)
250 unsigned int time_current = SDL_GetTicks();
251 unsigned int time_delayed = time_current + delay;
253 while (time_current < time_delayed)
255 // updating the screen contains waiting for frame delay (non-busy)
256 UpdateScreen_WithFrameDelay(NULL);
258 time_current = SDL_GetTicks();
262 static void SDLSetWindowIcon(char *basename)
264 /* (setting the window icon on Mac OS X would replace the high-quality
265 dock icon with the currently smaller (and uglier) icon from file) */
267 #if !defined(PLATFORM_MACOSX)
268 char *filename = getCustomImageFilename(basename);
269 SDL_Surface *surface;
271 if (filename == NULL)
273 Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
278 if ((surface = IMG_Load(filename)) == NULL)
280 Error(ERR_WARN, "IMG_Load() failed: %s", SDL_GetError());
285 /* set transparent color */
286 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
287 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
289 #if defined(TARGET_SDL2)
290 SDL_SetWindowIcon(sdl_window, surface);
292 SDL_WM_SetIcon(surface, NULL);
297 #if defined(TARGET_SDL2)
299 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
300 SDL_PixelFormat *format2)
302 return (format1->format == format2->format &&
303 format1->BitsPerPixel == format2->BitsPerPixel &&
304 format1->BytesPerPixel == format2->BytesPerPixel &&
305 format1->Rmask == format2->Rmask &&
306 format1->Gmask == format2->Gmask &&
307 format1->Bmask == format2->Bmask);
310 static Pixel SDLGetColorKey(SDL_Surface *surface)
314 if (SDL_GetColorKey(surface, &color_key) != 0)
320 static boolean SDLHasColorKey(SDL_Surface *surface)
322 return (SDLGetColorKey(surface) != -1);
325 static boolean SDLHasAlpha(SDL_Surface *surface)
327 SDL_BlendMode blend_mode;
329 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
332 return (blend_mode == SDL_BLENDMODE_BLEND);
335 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
337 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
339 SDL_SetSurfaceBlendMode(surface, blend_mode);
340 SDL_SetSurfaceAlphaMod(surface, alpha);
343 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
345 SDL_PixelFormat format;
346 SDL_Surface *new_surface;
351 if (backbuffer && backbuffer->surface)
353 format = *backbuffer->surface->format;
354 format.Amask = surface->format->Amask; // keep alpha channel
358 format = *surface->format;
361 new_surface = SDL_ConvertSurface(surface, &format, 0);
363 if (new_surface == NULL)
364 Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
369 boolean SDLSetNativeSurface(SDL_Surface **surface)
371 SDL_Surface *new_surface;
373 if (surface == NULL ||
375 backbuffer == NULL ||
376 backbuffer->surface == NULL)
379 // if pixel format already optimized for destination surface, do nothing
380 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
383 new_surface = SDLGetNativeSurface(*surface);
385 SDL_FreeSurface(*surface);
387 *surface = new_surface;
394 static Pixel SDLGetColorKey(SDL_Surface *surface)
396 if ((surface->flags & SDL_SRCCOLORKEY) == 0)
399 return surface->format->colorkey;
402 static boolean SDLHasColorKey(SDL_Surface *surface)
404 return (SDLGetColorKey(surface) != -1);
407 static boolean SDLHasAlpha(SDL_Surface *surface)
409 return ((surface->flags & SDL_SRCALPHA) != 0);
412 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
414 SDL_SetAlpha(surface, (set ? SDL_SRCALPHA : 0), alpha);
417 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
419 SDL_Surface *new_surface;
424 if (!video.initialized)
425 new_surface = SDL_ConvertSurface(surface, surface->format, SURFACE_FLAGS);
426 else if (SDLHasAlpha(surface))
427 new_surface = SDL_DisplayFormatAlpha(surface);
429 new_surface = SDL_DisplayFormat(surface);
431 if (new_surface == NULL)
432 Error(ERR_EXIT, "%s() failed: %s",
433 (video.initialized ? "SDL_DisplayFormat" : "SDL_ConvertSurface"),
439 boolean SDLSetNativeSurface(SDL_Surface **surface)
441 SDL_Surface *new_surface;
443 if (surface == NULL ||
448 new_surface = SDLGetNativeSurface(*surface);
450 SDL_FreeSurface(*surface);
452 *surface = new_surface;
459 #if defined(TARGET_SDL2)
460 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
462 if (program.headless)
465 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
468 Error(ERR_EXIT, "SDL_CreateTextureFromSurface() failed: %s",
475 void SDLCreateBitmapTextures(Bitmap *bitmap)
477 #if defined(TARGET_SDL2)
482 SDL_DestroyTexture(bitmap->texture);
483 if (bitmap->texture_masked)
484 SDL_DestroyTexture(bitmap->texture_masked);
486 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
487 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
491 void SDLFreeBitmapTextures(Bitmap *bitmap)
493 #if defined(TARGET_SDL2)
498 SDL_DestroyTexture(bitmap->texture);
499 if (bitmap->texture_masked)
500 SDL_DestroyTexture(bitmap->texture_masked);
502 bitmap->texture = NULL;
503 bitmap->texture_masked = NULL;
507 void SDLInitVideoDisplay(void)
509 #if !defined(TARGET_SDL2)
510 if (!strEqual(setup.system.sdl_videodriver, ARG_DEFAULT))
511 SDL_putenv(getStringCat2("SDL_VIDEODRIVER=", setup.system.sdl_videodriver));
513 SDL_putenv("SDL_VIDEO_CENTERED=1");
516 /* initialize SDL video */
517 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
518 Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
520 /* set default SDL depth */
521 #if !defined(TARGET_SDL2)
522 video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
524 video.default_depth = 32; // (how to determine video depth in SDL2?)
528 inline static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
530 if (program.headless)
533 video.window_scaling_percent = setup.window_scaling_percent;
534 video.window_scaling_quality = setup.window_scaling_quality;
536 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
538 #if defined(TARGET_SDL2)
539 // SDL 2.0: support for (desktop) fullscreen mode available
540 video.fullscreen_available = TRUE;
542 // SDL 1.2: no support for fullscreen mode in R'n'D anymore
543 video.fullscreen_available = FALSE;
546 /* open SDL video output device (window or fullscreen mode) */
547 if (!SDLSetVideoMode(fullscreen))
548 Error(ERR_EXIT, "setting video mode failed");
550 /* !!! SDL2 can only set the window icon if the window already exists !!! */
551 /* set window icon */
552 SDLSetWindowIcon(program.icon_filename);
554 /* set window and icon title */
558 inline static void SDLInitVideoBuffer_DrawBuffer()
560 /* SDL cannot directly draw to the visible video framebuffer like X11,
561 but always uses a backbuffer, which is then blitted to the visible
562 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
563 visible video framebuffer with 'SDL_Flip', if the hardware supports
564 this). Therefore do not use an additional backbuffer for drawing, but
565 use a symbolic buffer (distinguishable from the SDL backbuffer) called
566 'window', which indicates that the SDL backbuffer should be updated to
567 the visible video framebuffer when attempting to blit to it.
569 For convenience, it seems to be a good idea to create this symbolic
570 buffer 'window' at the same size as the SDL backbuffer. Although it
571 should never be drawn to directly, it would do no harm nevertheless. */
573 /* create additional (symbolic) buffer for double-buffering */
574 ReCreateBitmap(&window, video.width, video.height);
576 /* create dummy drawing buffer for headless mode, if needed */
577 if (program.headless)
578 ReCreateBitmap(&backbuffer, video.width, video.height);
581 void SDLInitVideoBuffer(boolean fullscreen)
583 SDLInitVideoBuffer_VideoBuffer(fullscreen);
584 SDLInitVideoBuffer_DrawBuffer();
587 static boolean SDLCreateScreen(boolean fullscreen)
589 SDL_Surface *new_surface = NULL;
591 #if defined(TARGET_SDL2)
592 int surface_flags_window = SURFACE_FLAGS | SDL_WINDOW_RESIZABLE;
593 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
595 int surface_flags_window = SURFACE_FLAGS;
596 int surface_flags_fullscreen = SURFACE_FLAGS; // (no fullscreen in SDL 1.2)
599 #if defined(TARGET_SDL2)
601 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
603 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
604 _without_ enabling 2D/3D acceleration and/or guest additions installed,
605 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
606 it will try to use accelerated graphics and apparently fails miserably) */
607 int renderer_flags = SDL_RENDERER_SOFTWARE;
610 SDLSetScreenSizeAndOffsets(video.width, video.height);
613 int width = video.width;
614 int height = video.height;
615 int screen_width = video.screen_width;
616 int screen_height = video.screen_height;
617 int surface_flags = (fullscreen ? surface_flags_fullscreen :
618 surface_flags_window);
620 // default window size is unscaled
621 video.window_width = screen_width;
622 video.window_height = screen_height;
624 #if defined(TARGET_SDL2)
626 // store if initial screen mode is fullscreen mode when changing screen size
627 video.fullscreen_initial = fullscreen;
629 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
631 video.window_width = window_scaling_factor * screen_width;
632 video.window_height = window_scaling_factor * screen_height;
634 if (sdl_texture_stream)
636 SDL_DestroyTexture(sdl_texture_stream);
637 sdl_texture_stream = NULL;
640 if (sdl_texture_target)
642 SDL_DestroyTexture(sdl_texture_target);
643 sdl_texture_target = NULL;
646 if (!(fullscreen && fullscreen_enabled))
650 SDL_DestroyRenderer(sdl_renderer);
656 SDL_DestroyWindow(sdl_window);
661 if (sdl_window == NULL)
662 sdl_window = SDL_CreateWindow(program.window_title,
663 SDL_WINDOWPOS_CENTERED,
664 SDL_WINDOWPOS_CENTERED,
669 if (sdl_window != NULL)
671 if (sdl_renderer == NULL)
672 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
674 if (sdl_renderer != NULL)
676 SDL_RenderSetLogicalSize(sdl_renderer, screen_width, screen_height);
677 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
678 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
680 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
681 SDL_PIXELFORMAT_ARGB8888,
682 SDL_TEXTUREACCESS_STREAMING,
685 if (SDL_RenderTargetSupported(sdl_renderer))
686 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
687 SDL_PIXELFORMAT_ARGB8888,
688 SDL_TEXTUREACCESS_TARGET,
691 if (sdl_texture_stream != NULL)
693 // use SDL default values for RGB masks and no alpha channel
694 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
696 if (new_surface == NULL)
697 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
701 Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
706 Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
711 Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
716 if (gfx.final_screen_bitmap == NULL)
717 gfx.final_screen_bitmap = CreateBitmapStruct();
719 gfx.final_screen_bitmap->width = width;
720 gfx.final_screen_bitmap->height = height;
722 gfx.final_screen_bitmap->surface =
723 SDL_SetVideoMode(width, height, video.depth, surface_flags);
725 if (gfx.final_screen_bitmap->surface != NULL)
728 SDL_CreateRGBSurface(surface_flags, width, height, video.depth, 0,0,0, 0);
730 if (new_surface == NULL)
731 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
734 new_surface = gfx.final_screen_bitmap->surface;
735 gfx.final_screen_bitmap = NULL;
741 Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
745 #if defined(TARGET_SDL2)
746 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
747 if (new_surface != NULL)
748 fullscreen_enabled = fullscreen;
751 if (backbuffer == NULL)
752 backbuffer = CreateBitmapStruct();
754 backbuffer->width = video.width;
755 backbuffer->height = video.height;
757 if (backbuffer->surface)
758 SDL_FreeSurface(backbuffer->surface);
760 backbuffer->surface = new_surface;
762 return (new_surface != NULL);
765 boolean SDLSetVideoMode(boolean fullscreen)
767 boolean success = FALSE;
771 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
773 /* switch display to fullscreen mode, if available */
774 success = SDLCreateScreen(TRUE);
778 /* switching display to fullscreen mode failed -- do not try it again */
779 video.fullscreen_available = FALSE;
783 video.fullscreen_enabled = TRUE;
787 if ((!fullscreen && video.fullscreen_enabled) || !success)
789 /* switch display to window mode */
790 success = SDLCreateScreen(FALSE);
794 /* switching display to window mode failed -- should not happen */
798 video.fullscreen_enabled = FALSE;
799 video.window_scaling_percent = setup.window_scaling_percent;
800 video.window_scaling_quality = setup.window_scaling_quality;
802 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
806 #if defined(TARGET_SDL2)
807 SDLRedrawWindow(); // map window
811 #if defined(PLATFORM_WIN32)
812 // experimental drag and drop code
814 SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
817 SDL_SysWMinfo wminfo;
819 boolean wminfo_success = FALSE;
821 SDL_VERSION(&wminfo.version);
822 #if defined(TARGET_SDL2)
824 wminfo_success = SDL_GetWindowWMInfo(sdl_window, &wminfo);
826 wminfo_success = (SDL_GetWMInfo(&wminfo) == 1);
831 #if defined(TARGET_SDL2)
832 hwnd = wminfo.info.win.window;
834 hwnd = wminfo.window;
837 DragAcceptFiles(hwnd, TRUE);
846 void SDLSetWindowTitle()
848 #if defined(TARGET_SDL2)
849 if (sdl_window == NULL)
852 SDL_SetWindowTitle(sdl_window, program.window_title);
854 SDL_WM_SetCaption(program.window_title, program.window_title);
858 #if defined(TARGET_SDL2)
859 void SDLSetWindowScaling(int window_scaling_percent)
861 if (sdl_window == NULL)
864 float window_scaling_factor = (float)window_scaling_percent / 100;
865 int new_window_width = (int)(window_scaling_factor * video.screen_width);
866 int new_window_height = (int)(window_scaling_factor * video.screen_height);
868 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
870 video.window_scaling_percent = window_scaling_percent;
871 video.window_width = new_window_width;
872 video.window_height = new_window_height;
877 void SDLSetWindowScalingQuality(char *window_scaling_quality)
879 SDL_Texture *new_texture;
881 if (sdl_texture_stream == NULL)
884 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
886 new_texture = SDL_CreateTexture(sdl_renderer,
887 SDL_PIXELFORMAT_ARGB8888,
888 SDL_TEXTUREACCESS_STREAMING,
889 video.width, video.height);
891 if (new_texture != NULL)
893 SDL_DestroyTexture(sdl_texture_stream);
895 sdl_texture_stream = new_texture;
898 if (SDL_RenderTargetSupported(sdl_renderer))
899 new_texture = SDL_CreateTexture(sdl_renderer,
900 SDL_PIXELFORMAT_ARGB8888,
901 SDL_TEXTUREACCESS_TARGET,
902 video.width, video.height);
906 if (new_texture != NULL)
908 SDL_DestroyTexture(sdl_texture_target);
910 sdl_texture_target = new_texture;
915 video.window_scaling_quality = window_scaling_quality;
918 void SDLSetWindowFullscreen(boolean fullscreen)
920 if (sdl_window == NULL)
923 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
925 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
926 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
928 // if screen size was changed in fullscreen mode, correct desktop window size
929 if (!fullscreen && video.fullscreen_initial)
931 SDLSetWindowScaling(setup.window_scaling_percent);
932 SDL_SetWindowPosition(sdl_window,
933 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
935 video.fullscreen_initial = FALSE;
939 void SDLSetDisplaySize()
941 SDL_Rect display_bounds;
943 SDL_GetDisplayBounds(0, &display_bounds);
945 video.display_width = display_bounds.w;
946 video.display_height = display_bounds.h;
949 Error(ERR_DEBUG, "SDL real screen size: %d x %d",
950 video.display_width, video.display_height);
954 void SDLSetScreenSizeAndOffsets(int width, int height)
956 // set default video screen size and offsets
957 video.screen_width = width;
958 video.screen_height = height;
959 video.screen_xoffset = 0;
960 video.screen_yoffset = 0;
962 #if defined(USE_COMPLETE_DISPLAY)
963 float ratio_video = (float) width / height;
964 float ratio_display = (float) video.display_width / video.display_height;
966 if (ratio_video != ratio_display)
968 // adjust drawable screen size to cover the whole device display
970 if (ratio_video < ratio_display)
971 video.screen_width *= ratio_display / ratio_video;
973 video.screen_height *= ratio_video / ratio_display;
975 video.screen_xoffset = (video.screen_width - width) / 2;
976 video.screen_yoffset = (video.screen_height - height) / 2;
979 Error(ERR_DEBUG, "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
981 video.screen_width, video.screen_height,
982 ratio_video, ratio_display);
988 void SDLSetScreenSizeForRenderer(int width, int height)
990 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
993 void SDLSetScreenProperties()
995 SDLSetScreenSizeAndOffsets(video.width, video.height);
996 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
1001 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
1003 #if defined(TARGET_SDL2)
1004 video.screen_rendering_mode =
1005 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
1006 SPECIAL_RENDERING_BITMAP :
1007 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
1008 SPECIAL_RENDERING_TARGET:
1009 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
1010 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
1012 video.screen_rendering_mode = SPECIAL_RENDERING_BITMAP;
1016 void SDLRedrawWindow()
1018 UpdateScreen_WithoutFrameDelay(NULL);
1021 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
1024 if (program.headless)
1027 SDL_Surface *surface =
1028 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
1030 if (surface == NULL)
1031 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
1033 SDLSetNativeSurface(&surface);
1035 bitmap->surface = surface;
1038 void SDLFreeBitmapPointers(Bitmap *bitmap)
1040 if (bitmap->surface)
1041 SDL_FreeSurface(bitmap->surface);
1042 if (bitmap->surface_masked)
1043 SDL_FreeSurface(bitmap->surface_masked);
1045 bitmap->surface = NULL;
1046 bitmap->surface_masked = NULL;
1048 #if defined(TARGET_SDL2)
1049 if (bitmap->texture)
1050 SDL_DestroyTexture(bitmap->texture);
1051 if (bitmap->texture_masked)
1052 SDL_DestroyTexture(bitmap->texture_masked);
1054 bitmap->texture = NULL;
1055 bitmap->texture_masked = NULL;
1059 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1060 int src_x, int src_y, int width, int height,
1061 int dst_x, int dst_y, int mask_mode)
1063 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1064 SDL_Rect src_rect, dst_rect;
1069 src_rect.h = height;
1074 dst_rect.h = height;
1076 // if (src_bitmap != backbuffer || dst_bitmap != window)
1077 if (!(src_bitmap == backbuffer && dst_bitmap == window))
1078 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
1079 src_bitmap->surface_masked : src_bitmap->surface),
1080 &src_rect, real_dst_bitmap->surface, &dst_rect);
1082 if (dst_bitmap == window)
1083 UpdateScreen_WithFrameDelay(&dst_rect);
1086 void SDLBlitTexture(Bitmap *bitmap,
1087 int src_x, int src_y, int width, int height,
1088 int dst_x, int dst_y, int mask_mode)
1090 #if defined(TARGET_SDL2)
1091 SDL_Texture *texture;
1096 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1098 if (texture == NULL)
1104 src_rect.h = height;
1109 dst_rect.h = height;
1111 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1115 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1118 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1126 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1128 if (dst_bitmap == window)
1129 UpdateScreen_WithFrameDelay(&rect);
1132 void PrepareFadeBitmap(int draw_target)
1134 Bitmap *fade_bitmap =
1135 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1136 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1138 if (fade_bitmap == NULL)
1141 // copy backbuffer to fading buffer
1142 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1144 // add border and animations to fading buffer
1145 FinalizeScreen(draw_target);
1148 void SDLFadeRectangle(int x, int y, int width, int height,
1149 int fade_mode, int fade_delay, int post_delay,
1150 void (*draw_border_function)(void))
1152 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1153 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1154 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1155 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1156 SDL_Surface *surface_screen = backbuffer->surface;
1157 SDL_Rect src_rect, dst_rect;
1159 int src_x = x, src_y = y;
1160 int dst_x = x, dst_y = y;
1161 unsigned int time_last, time_current;
1163 // store function for drawing global masked border
1164 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1166 // deactivate drawing of global border while fading, if needed
1167 if (draw_border_function == NULL)
1168 gfx.draw_global_border_function = NULL;
1173 src_rect.h = height;
1177 dst_rect.w = width; /* (ignored) */
1178 dst_rect.h = height; /* (ignored) */
1180 dst_rect2 = dst_rect;
1182 // before fading in, store backbuffer (without animation graphics)
1183 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1184 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1186 /* copy source and target surfaces to temporary surfaces for fading */
1187 if (fade_mode & FADE_TYPE_TRANSFORM)
1189 // (source and target fading buffer already prepared)
1191 else if (fade_mode & FADE_TYPE_FADE_IN)
1193 // (target fading buffer already prepared)
1194 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1196 else /* FADE_TYPE_FADE_OUT */
1198 // (source fading buffer already prepared)
1199 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1202 time_current = SDL_GetTicks();
1204 if (fade_mode == FADE_MODE_MELT)
1206 boolean done = FALSE;
1207 int melt_pixels = 2;
1208 int melt_columns = width / melt_pixels;
1209 int ypos[melt_columns];
1210 int max_steps = height / 8 + 32;
1215 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1217 SDLSetAlpha(surface_target, FALSE, 0); /* disable alpha blending */
1219 ypos[0] = -GetSimpleRandom(16);
1221 for (i = 1 ; i < melt_columns; i++)
1223 int r = GetSimpleRandom(3) - 1; /* randomly choose from { -1, 0, -1 } */
1225 ypos[i] = ypos[i - 1] + r;
1238 time_last = time_current;
1239 time_current = SDL_GetTicks();
1240 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1241 steps_final = MIN(MAX(0, steps), max_steps);
1245 done = (steps_done >= steps_final);
1247 for (i = 0 ; i < melt_columns; i++)
1255 else if (ypos[i] < height)
1260 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1262 if (ypos[i] + dy >= height)
1263 dy = height - ypos[i];
1265 /* copy part of (appearing) target surface to upper area */
1266 src_rect.x = src_x + i * melt_pixels;
1267 // src_rect.y = src_y + ypos[i];
1269 src_rect.w = melt_pixels;
1271 src_rect.h = ypos[i] + dy;
1273 dst_rect.x = dst_x + i * melt_pixels;
1274 // dst_rect.y = dst_y + ypos[i];
1277 if (steps_done >= steps_final)
1278 SDL_BlitSurface(surface_target, &src_rect,
1279 surface_screen, &dst_rect);
1283 /* copy part of (disappearing) source surface to lower area */
1284 src_rect.x = src_x + i * melt_pixels;
1286 src_rect.w = melt_pixels;
1287 src_rect.h = height - ypos[i];
1289 dst_rect.x = dst_x + i * melt_pixels;
1290 dst_rect.y = dst_y + ypos[i];
1292 if (steps_done >= steps_final)
1293 SDL_BlitSurface(surface_source, &src_rect,
1294 surface_screen, &dst_rect);
1300 src_rect.x = src_x + i * melt_pixels;
1302 src_rect.w = melt_pixels;
1303 src_rect.h = height;
1305 dst_rect.x = dst_x + i * melt_pixels;
1308 if (steps_done >= steps_final)
1309 SDL_BlitSurface(surface_target, &src_rect,
1310 surface_screen, &dst_rect);
1314 if (steps_done >= steps_final)
1316 if (draw_border_function != NULL)
1317 draw_border_function();
1319 UpdateScreen_WithFrameDelay(&dst_rect2);
1323 else if (fade_mode == FADE_MODE_CURTAIN)
1327 int xx_size = width / 2;
1329 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1331 SDLSetAlpha(surface_source, FALSE, 0); /* disable alpha blending */
1333 for (xx = 0; xx < xx_size;)
1335 time_last = time_current;
1336 time_current = SDL_GetTicks();
1337 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1338 xx_final = MIN(MAX(0, xx), xx_size);
1343 src_rect.h = height;
1348 /* draw new (target) image to screen buffer */
1349 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1351 if (xx_final < xx_size)
1353 src_rect.w = xx_size - xx_final;
1354 src_rect.h = height;
1356 /* draw old (source) image to screen buffer (left side) */
1358 src_rect.x = src_x + xx_final;
1361 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1363 /* draw old (source) image to screen buffer (right side) */
1365 src_rect.x = src_x + xx_size;
1366 dst_rect.x = dst_x + xx_size + xx_final;
1368 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1371 if (draw_border_function != NULL)
1372 draw_border_function();
1374 /* only update the region of the screen that is affected from fading */
1375 UpdateScreen_WithFrameDelay(&dst_rect2);
1378 else /* fading in, fading out or cross-fading */
1383 for (alpha = 0.0; alpha < 255.0;)
1385 time_last = time_current;
1386 time_current = SDL_GetTicks();
1387 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1388 alpha_final = MIN(MAX(0, alpha), 255);
1390 /* draw existing (source) image to screen buffer */
1391 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1393 /* draw new (target) image to screen buffer using alpha blending */
1394 SDLSetAlpha(surface_target, TRUE, alpha_final);
1395 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1397 if (draw_border_function != NULL)
1398 draw_border_function();
1400 /* only update the region of the screen that is affected from fading */
1401 UpdateScreen_WithFrameDelay(&dst_rect);
1406 Delay_WithScreenUpdates(post_delay);
1408 // restore function for drawing global masked border
1409 gfx.draw_global_border_function = draw_global_border_function;
1411 // after fading in, restore backbuffer (without animation graphics)
1412 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1413 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1416 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1417 int to_x, int to_y, Uint32 color)
1419 SDL_Surface *surface = dst_bitmap->surface;
1423 swap_numbers(&from_x, &to_x);
1426 swap_numbers(&from_y, &to_y);
1430 rect.w = (to_x - from_x + 1);
1431 rect.h = (to_y - from_y + 1);
1433 SDL_FillRect(surface, &rect, color);
1436 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1437 int to_x, int to_y, Uint32 color)
1439 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1442 #if ENABLE_UNUSED_CODE
1443 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1444 int num_points, Uint32 color)
1449 for (i = 0; i < num_points - 1; i++)
1451 for (x = 0; x < line_width; x++)
1453 for (y = 0; y < line_width; y++)
1455 int dx = x - line_width / 2;
1456 int dy = y - line_width / 2;
1458 if ((x == 0 && y == 0) ||
1459 (x == 0 && y == line_width - 1) ||
1460 (x == line_width - 1 && y == 0) ||
1461 (x == line_width - 1 && y == line_width - 1))
1464 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1465 points[i+1].x + dx, points[i+1].y + dy, color);
1472 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1474 SDL_Surface *surface = src_bitmap->surface;
1476 switch (surface->format->BytesPerPixel)
1478 case 1: /* assuming 8-bpp */
1480 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1484 case 2: /* probably 15-bpp or 16-bpp */
1486 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1490 case 3: /* slow 24-bpp mode; usually not used */
1492 /* does this work? */
1493 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1497 shift = surface->format->Rshift;
1498 color |= *(pix + shift / 8) >> shift;
1499 shift = surface->format->Gshift;
1500 color |= *(pix + shift / 8) >> shift;
1501 shift = surface->format->Bshift;
1502 color |= *(pix + shift / 8) >> shift;
1508 case 4: /* probably 32-bpp */
1510 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1519 /* ========================================================================= */
1520 /* The following functions were taken from the SGE library */
1521 /* (SDL Graphics Extension Library) by Anders Lindström */
1522 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html */
1523 /* ========================================================================= */
1525 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1527 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1529 switch (surface->format->BytesPerPixel)
1533 /* Assuming 8-bpp */
1534 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1540 /* Probably 15-bpp or 16-bpp */
1541 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1547 /* Slow 24-bpp mode, usually not used */
1551 /* Gack - slow, but endian correct */
1552 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1553 shift = surface->format->Rshift;
1554 *(pix+shift/8) = color>>shift;
1555 shift = surface->format->Gshift;
1556 *(pix+shift/8) = color>>shift;
1557 shift = surface->format->Bshift;
1558 *(pix+shift/8) = color>>shift;
1564 /* Probably 32-bpp */
1565 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1572 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1573 Uint8 R, Uint8 G, Uint8 B)
1575 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1578 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1580 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1583 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1585 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1588 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1593 /* Gack - slow, but endian correct */
1594 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1595 shift = surface->format->Rshift;
1596 *(pix+shift/8) = color>>shift;
1597 shift = surface->format->Gshift;
1598 *(pix+shift/8) = color>>shift;
1599 shift = surface->format->Bshift;
1600 *(pix+shift/8) = color>>shift;
1603 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1605 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1608 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1610 switch (dest->format->BytesPerPixel)
1613 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1617 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1621 _PutPixel24(dest,x,y,color);
1625 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1630 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1632 if (SDL_MUSTLOCK(surface))
1634 if (SDL_LockSurface(surface) < 0)
1640 _PutPixel(surface, x, y, color);
1642 if (SDL_MUSTLOCK(surface))
1644 SDL_UnlockSurface(surface);
1648 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1649 Uint8 r, Uint8 g, Uint8 b)
1651 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1654 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1656 if (y >= 0 && y <= dest->h - 1)
1658 switch (dest->format->BytesPerPixel)
1661 return y*dest->pitch;
1665 return y*dest->pitch/2;
1669 return y*dest->pitch;
1673 return y*dest->pitch/4;
1681 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
1683 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1685 switch (surface->format->BytesPerPixel)
1689 /* Assuming 8-bpp */
1690 *((Uint8 *)surface->pixels + ypitch + x) = color;
1696 /* Probably 15-bpp or 16-bpp */
1697 *((Uint16 *)surface->pixels + ypitch + x) = color;
1703 /* Slow 24-bpp mode, usually not used */
1707 /* Gack - slow, but endian correct */
1708 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1709 shift = surface->format->Rshift;
1710 *(pix+shift/8) = color>>shift;
1711 shift = surface->format->Gshift;
1712 *(pix+shift/8) = color>>shift;
1713 shift = surface->format->Bshift;
1714 *(pix+shift/8) = color>>shift;
1720 /* Probably 32-bpp */
1721 *((Uint32 *)surface->pixels + ypitch + x) = color;
1728 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1733 if (SDL_MUSTLOCK(Surface))
1735 if (SDL_LockSurface(Surface) < 0)
1748 /* Do the clipping */
1749 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1753 if (x2 > Surface->w - 1)
1754 x2 = Surface->w - 1;
1761 SDL_FillRect(Surface, &l, Color);
1763 if (SDL_MUSTLOCK(Surface))
1765 SDL_UnlockSurface(Surface);
1769 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1770 Uint8 R, Uint8 G, Uint8 B)
1772 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1775 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
1786 /* Do the clipping */
1787 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1791 if (x2 > Surface->w - 1)
1792 x2 = Surface->w - 1;
1799 SDL_FillRect(Surface, &l, Color);
1802 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1807 if (SDL_MUSTLOCK(Surface))
1809 if (SDL_LockSurface(Surface) < 0)
1822 /* Do the clipping */
1823 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1827 if (y2 > Surface->h - 1)
1828 y2 = Surface->h - 1;
1835 SDL_FillRect(Surface, &l, Color);
1837 if (SDL_MUSTLOCK(Surface))
1839 SDL_UnlockSurface(Surface);
1843 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1844 Uint8 R, Uint8 G, Uint8 B)
1846 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1849 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
1860 /* Do the clipping */
1861 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1865 if (y2 > Surface->h - 1)
1866 y2 = Surface->h - 1;
1873 SDL_FillRect(Surface, &l, Color);
1876 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1877 Sint16 x2, Sint16 y2, Uint32 Color,
1878 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1881 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1886 sdx = (dx < 0) ? -1 : 1;
1887 sdy = (dy < 0) ? -1 : 1;
1899 for (x = 0; x < dx; x++)
1901 Callback(Surface, px, py, Color);
1915 for (y = 0; y < dy; y++)
1917 Callback(Surface, px, py, Color);
1931 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1932 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1933 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1936 sge_DoLine(Surface, X1, Y1, X2, Y2,
1937 SDL_MapRGB(Surface->format, R, G, B), Callback);
1940 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1943 if (SDL_MUSTLOCK(Surface))
1945 if (SDL_LockSurface(Surface) < 0)
1950 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1952 /* unlock the display */
1953 if (SDL_MUSTLOCK(Surface))
1955 SDL_UnlockSurface(Surface);
1959 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1960 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1962 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1965 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1967 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1972 -----------------------------------------------------------------------------
1973 quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1974 -----------------------------------------------------------------------------
1977 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1978 int width, int height, Uint32 color)
1982 for (y = src_y; y < src_y + height; y++)
1984 for (x = src_x; x < src_x + width; x++)
1986 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1988 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1993 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1994 int src_x, int src_y, int width, int height,
1995 int dst_x, int dst_y)
1999 for (y = 0; y < height; y++)
2001 for (x = 0; x < width; x++)
2003 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
2005 if (pixel != BLACK_PIXEL)
2006 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
2012 /* ========================================================================= */
2013 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
2014 /* (Rotozoomer) by Andreas Schiffler */
2015 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html */
2016 /* ========================================================================= */
2019 -----------------------------------------------------------------------------
2022 zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
2023 -----------------------------------------------------------------------------
2034 int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
2037 tColorRGBA *sp, *csp, *dp;
2041 sp = csp = (tColorRGBA *) src->pixels;
2042 dp = (tColorRGBA *) dst->pixels;
2043 dgap = dst->pitch - dst->w * 4;
2045 for (y = 0; y < dst->h; y++)
2049 for (x = 0; x < dst->w; x++)
2051 tColorRGBA *sp0 = sp;
2052 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
2053 tColorRGBA *sp00 = &sp0[0];
2054 tColorRGBA *sp01 = &sp0[1];
2055 tColorRGBA *sp10 = &sp1[0];
2056 tColorRGBA *sp11 = &sp1[1];
2059 /* create new color pixel from all four source color pixels */
2060 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
2061 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
2062 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
2063 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
2068 /* advance source pointers */
2071 /* advance destination pointer */
2075 /* advance source pointer */
2076 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2078 /* advance destination pointers */
2079 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2085 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2087 int x, y, *sax, *say, *csax, *csay;
2089 tColorRGBA *sp, *csp, *csp0, *dp;
2092 /* use specialized zoom function when scaling down to exactly half size */
2093 if (src->w == 2 * dst->w &&
2094 src->h == 2 * dst->h)
2095 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2097 /* variable setup */
2098 sx = (float) src->w / (float) dst->w;
2099 sy = (float) src->h / (float) dst->h;
2101 /* allocate memory for row increments */
2102 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2103 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2105 /* precalculate row increments */
2106 for (x = 0; x <= dst->w; x++)
2107 *csax++ = (int)(sx * x);
2109 for (y = 0; y <= dst->h; y++)
2110 *csay++ = (int)(sy * y);
2113 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2114 dp = (tColorRGBA *) dst->pixels;
2115 dgap = dst->pitch - dst->w * 4;
2118 for (y = 0; y < dst->h; y++)
2123 for (x = 0; x < dst->w; x++)
2128 /* advance source pointers */
2132 /* advance destination pointer */
2136 /* advance source pointer */
2138 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2140 /* advance destination pointers */
2141 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2151 -----------------------------------------------------------------------------
2154 zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2155 -----------------------------------------------------------------------------
2158 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2160 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2161 Uint8 *sp, *dp, *csp;
2164 /* variable setup */
2165 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2166 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2168 /* allocate memory for row increments */
2169 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2170 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2172 /* precalculate row increments */
2175 for (x = 0; x < dst->w; x++)
2178 *csax = (csx >> 16);
2185 for (y = 0; y < dst->h; y++)
2188 *csay = (csy >> 16);
2195 for (x = 0; x < dst->w; x++)
2203 for (y = 0; y < dst->h; y++)
2210 sp = csp = (Uint8 *) src->pixels;
2211 dp = (Uint8 *) dst->pixels;
2212 dgap = dst->pitch - dst->w;
2216 for (y = 0; y < dst->h; y++)
2220 for (x = 0; x < dst->w; x++)
2225 /* advance source pointers */
2229 /* advance destination pointer */
2233 /* advance source pointer (for row) */
2234 csp += ((*csay) * src->pitch);
2237 /* advance destination pointers */
2248 -----------------------------------------------------------------------------
2251 Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2252 'zoomx' and 'zoomy' are scaling factors for width and height.
2253 If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2254 into a 32bit RGBA format on the fly.
2255 -----------------------------------------------------------------------------
2258 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2260 SDL_Surface *zoom_src = NULL;
2261 SDL_Surface *zoom_dst = NULL;
2262 boolean is_converted = FALSE;
2269 /* determine if source surface is 32 bit or 8 bit */
2270 is_32bit = (src->format->BitsPerPixel == 32);
2272 if (is_32bit || src->format->BitsPerPixel == 8)
2274 /* use source surface 'as is' */
2279 /* new source surface is 32 bit with a defined RGB ordering */
2280 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2281 0x000000ff, 0x0000ff00, 0x00ff0000,
2282 (src->format->Amask ? 0xff000000 : 0));
2283 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2285 is_converted = TRUE;
2288 /* allocate surface to completely contain the zoomed surface */
2291 /* target surface is 32 bit with source RGBA/ABGR ordering */
2292 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2293 zoom_src->format->Rmask,
2294 zoom_src->format->Gmask,
2295 zoom_src->format->Bmask,
2296 zoom_src->format->Amask);
2300 /* target surface is 8 bit */
2301 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2305 /* lock source surface */
2306 SDL_LockSurface(zoom_src);
2308 /* check which kind of surface we have */
2311 /* call the 32 bit transformation routine to do the zooming */
2312 zoomSurfaceRGBA(zoom_src, zoom_dst);
2317 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2318 zoom_dst->format->palette->colors[i] =
2319 zoom_src->format->palette->colors[i];
2320 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2322 /* call the 8 bit transformation routine to do the zooming */
2323 zoomSurfaceY(zoom_src, zoom_dst);
2326 /* unlock source surface */
2327 SDL_UnlockSurface(zoom_src);
2329 /* free temporary surface */
2331 SDL_FreeSurface(zoom_src);
2333 /* return destination surface */
2337 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2339 SDL_Surface *new_surface;
2341 if (surface == NULL)
2344 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2345 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2347 /* remove alpha channel from native non-transparent surface, if defined */
2348 SDLSetAlpha(new_surface, FALSE, 0);
2350 /* remove transparent color from native non-transparent surface, if defined */
2351 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2356 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2358 Bitmap *dst_bitmap = CreateBitmapStruct();
2359 SDL_Surface *src_surface = src_bitmap->surface_masked;
2360 SDL_Surface *dst_surface;
2362 dst_width = MAX(1, dst_width); /* prevent zero bitmap width */
2363 dst_height = MAX(1, dst_height); /* prevent zero bitmap height */
2365 dst_bitmap->width = dst_width;
2366 dst_bitmap->height = dst_height;
2368 /* create zoomed temporary surface from source surface */
2369 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2371 /* create native format destination surface from zoomed temporary surface */
2372 SDLSetNativeSurface(&dst_surface);
2374 /* set color key for zoomed surface from source surface, if defined */
2375 if (SDLHasColorKey(src_surface))
2376 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2377 SDLGetColorKey(src_surface));
2379 /* create native non-transparent surface for opaque blitting */
2380 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2382 /* set native transparent surface for masked blitting */
2383 dst_bitmap->surface_masked = dst_surface;
2389 /* ========================================================================= */
2390 /* load image to bitmap */
2391 /* ========================================================================= */
2393 Bitmap *SDLLoadImage(char *filename)
2395 Bitmap *new_bitmap = CreateBitmapStruct();
2396 SDL_Surface *sdl_image_tmp;
2398 if (program.headless)
2400 /* prevent sanity check warnings at later stage */
2401 new_bitmap->width = new_bitmap->height = 1;
2406 print_timestamp_init("SDLLoadImage");
2408 print_timestamp_time(getBaseNamePtr(filename));
2410 /* load image to temporary surface */
2411 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2412 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
2414 print_timestamp_time("IMG_Load");
2416 UPDATE_BUSY_STATE();
2418 /* create native non-transparent surface for current image */
2419 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2420 Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2422 print_timestamp_time("SDLGetNativeSurface (opaque)");
2424 UPDATE_BUSY_STATE();
2426 /* set black pixel to transparent if no alpha channel / transparent color */
2427 if (!SDLHasAlpha(sdl_image_tmp) &&
2428 !SDLHasColorKey(sdl_image_tmp))
2429 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2430 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2432 /* create native transparent surface for current image */
2433 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2434 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2436 print_timestamp_time("SDLGetNativeSurface (masked)");
2438 UPDATE_BUSY_STATE();
2440 /* free temporary surface */
2441 SDL_FreeSurface(sdl_image_tmp);
2443 new_bitmap->width = new_bitmap->surface->w;
2444 new_bitmap->height = new_bitmap->surface->h;
2446 print_timestamp_done("SDLLoadImage");
2452 /* ------------------------------------------------------------------------- */
2453 /* custom cursor fuctions */
2454 /* ------------------------------------------------------------------------- */
2456 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2458 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2459 cursor_info->width, cursor_info->height,
2460 cursor_info->hot_x, cursor_info->hot_y);
2463 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2465 static struct MouseCursorInfo *last_cursor_info = NULL;
2466 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2467 static SDL_Cursor *cursor_default = NULL;
2468 static SDL_Cursor *cursor_current = NULL;
2470 /* if invoked for the first time, store the SDL default cursor */
2471 if (cursor_default == NULL)
2472 cursor_default = SDL_GetCursor();
2474 /* only create new cursor if cursor info (custom only) has changed */
2475 if (cursor_info != NULL && cursor_info != last_cursor_info)
2477 cursor_current = create_cursor(cursor_info);
2478 last_cursor_info = cursor_info;
2481 /* only set new cursor if cursor info (custom or NULL) has changed */
2482 if (cursor_info != last_cursor_info2)
2483 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2485 last_cursor_info2 = cursor_info;
2489 /* ========================================================================= */
2490 /* audio functions */
2491 /* ========================================================================= */
2493 void SDLOpenAudio(void)
2495 if (program.headless)
2498 #if !defined(TARGET_SDL2)
2499 if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
2500 SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
2503 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2505 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2509 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2510 AUDIO_NUM_CHANNELS_STEREO,
2511 setup.system.audio_fragment_size) < 0)
2513 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2517 audio.sound_available = TRUE;
2518 audio.music_available = TRUE;
2519 audio.loops_available = TRUE;
2520 audio.sound_enabled = TRUE;
2522 /* set number of available mixer channels */
2523 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2524 audio.music_channel = MUSIC_CHANNEL;
2525 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2527 Mixer_InitChannels();
2530 void SDLCloseAudio(void)
2533 Mix_HaltChannel(-1);
2536 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2540 /* ========================================================================= */
2541 /* event functions */
2542 /* ========================================================================= */
2544 void SDLWaitEvent(Event *event)
2546 SDL_WaitEvent(event);
2549 void SDLHandleWindowManagerEvent(Event *event)
2552 #if defined(PLATFORM_WIN32)
2553 // experimental drag and drop code
2555 SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
2556 SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
2558 #if defined(TARGET_SDL2)
2559 if (syswmmsg->msg.win.msg == WM_DROPFILES)
2561 if (syswmmsg->msg == WM_DROPFILES)
2564 #if defined(TARGET_SDL2)
2565 HDROP hdrop = (HDROP)syswmmsg->msg.win.wParam;
2567 HDROP hdrop = (HDROP)syswmmsg->wParam;
2571 printf("::: SDL_SYSWMEVENT:\n");
2573 num_files = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
2575 for (i = 0; i < num_files; i++)
2577 int buffer_len = DragQueryFile(hdrop, i, NULL, 0);
2578 char buffer[buffer_len + 1];
2580 DragQueryFile(hdrop, i, buffer, buffer_len + 1);
2582 printf("::: - '%s'\n", buffer);
2585 #if defined(TARGET_SDL2)
2586 DragFinish((HDROP)syswmmsg->msg.win.wParam);
2588 DragFinish((HDROP)syswmmsg->wParam);
2596 /* ========================================================================= */
2597 /* joystick functions */
2598 /* ========================================================================= */
2600 #if defined(TARGET_SDL2)
2601 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2603 static SDL_Joystick *sdl_joystick[MAX_PLAYERS]; // only joysticks supported
2605 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2606 static int sdl_js_axis[MAX_PLAYERS][2];
2607 static int sdl_js_button[MAX_PLAYERS][2];
2608 static boolean sdl_is_controller[MAX_PLAYERS];
2610 void SDLClearJoystickState()
2614 for (i = 0; i < MAX_PLAYERS; i++)
2616 for (j = 0; j < 2; j++)
2618 sdl_js_axis_raw[i][j] = -1;
2619 sdl_js_axis[i][j] = 0;
2620 sdl_js_button[i][j] = 0;
2625 boolean SDLOpenJoystick(int nr)
2627 if (nr < 0 || nr >= MAX_PLAYERS)
2630 #if defined(TARGET_SDL2)
2631 sdl_is_controller[nr] = SDL_IsGameController(nr);
2633 sdl_is_controller[nr] = FALSE;
2637 Error(ERR_DEBUG, "opening joystick %d (%s)",
2638 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2641 #if defined(TARGET_SDL2)
2642 if (sdl_is_controller[nr])
2643 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2645 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2647 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2650 return (sdl_joystick[nr] != NULL);
2653 void SDLCloseJoystick(int nr)
2655 if (nr < 0 || nr >= MAX_PLAYERS)
2659 Error(ERR_DEBUG, "closing joystick %d", nr);
2662 #if defined(TARGET_SDL2)
2663 if (sdl_is_controller[nr])
2664 SDL_GameControllerClose(sdl_joystick[nr]);
2666 SDL_JoystickClose(sdl_joystick[nr]);
2668 SDL_JoystickClose(sdl_joystick[nr]);
2671 sdl_joystick[nr] = NULL;
2674 boolean SDLCheckJoystickOpened(int nr)
2676 if (nr < 0 || nr >= MAX_PLAYERS)
2679 #if defined(TARGET_SDL2)
2680 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2682 return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
2686 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2688 #if defined(TARGET_SDL2)
2689 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2690 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2691 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2692 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2694 int axis_id = axis_id_raw % 2;
2697 if (nr < 0 || nr >= MAX_PLAYERS)
2703 // prevent (slightly jittering, but centered) axis A from resetting axis B
2704 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2705 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2708 sdl_js_axis[nr][axis_id] = axis_value;
2709 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2712 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2714 #if defined(TARGET_SDL2)
2715 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2716 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2717 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2718 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2719 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2720 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2721 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2722 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2725 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2726 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2727 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2728 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2729 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2730 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2731 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2732 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2734 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2735 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2736 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2737 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2738 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2740 int button_id = button_id_raw % 2;
2743 if (nr < 0 || nr >= MAX_PLAYERS)
2746 if (button_id == -1)
2749 sdl_js_button[nr][button_id] = button_state;
2752 void HandleJoystickEvent(Event *event)
2756 #if defined(TARGET_SDL2)
2757 case SDL_CONTROLLERDEVICEADDED:
2759 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2760 event->cdevice.which);
2765 case SDL_CONTROLLERDEVICEREMOVED:
2767 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2768 event->cdevice.which);
2773 case SDL_CONTROLLERAXISMOTION:
2775 Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2776 event->caxis.which, event->caxis.axis, event->caxis.value);
2778 setJoystickAxis(event->caxis.which,
2780 event->caxis.value);
2783 case SDL_CONTROLLERBUTTONDOWN:
2785 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2786 event->cbutton.which, event->cbutton.button);
2788 setJoystickButton(event->cbutton.which,
2789 event->cbutton.button,
2793 case SDL_CONTROLLERBUTTONUP:
2795 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2796 event->cbutton.which, event->cbutton.button);
2798 setJoystickButton(event->cbutton.which,
2799 event->cbutton.button,
2804 case SDL_JOYAXISMOTION:
2805 if (sdl_is_controller[event->jaxis.which])
2809 Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2810 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2812 if (event->jaxis.axis < 4)
2813 setJoystickAxis(event->jaxis.which,
2815 event->jaxis.value);
2818 case SDL_JOYBUTTONDOWN:
2819 if (sdl_is_controller[event->jaxis.which])
2823 Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2824 event->jbutton.which, event->jbutton.button);
2826 if (event->jbutton.button < 4)
2827 setJoystickButton(event->jbutton.which,
2828 event->jbutton.button,
2832 case SDL_JOYBUTTONUP:
2833 if (sdl_is_controller[event->jaxis.which])
2837 Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2838 event->jbutton.which, event->jbutton.button);
2840 if (event->jbutton.button < 4)
2841 setJoystickButton(event->jbutton.which,
2842 event->jbutton.button,
2851 void SDLInitJoysticks()
2853 static boolean sdl_joystick_subsystem_initialized = FALSE;
2854 boolean print_warning = !sdl_joystick_subsystem_initialized;
2855 #if defined(TARGET_SDL2)
2856 char *mappings_file_base = getPath2(options.conf_directory,
2857 GAMECONTROLLER_BASENAME);
2858 char *mappings_file_user = getPath2(getUserGameDataDir(),
2859 GAMECONTROLLER_BASENAME);
2864 if (!sdl_joystick_subsystem_initialized)
2866 sdl_joystick_subsystem_initialized = TRUE;
2868 #if defined(TARGET_SDL2)
2869 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2871 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2873 if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
2876 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2880 #if defined(TARGET_SDL2)
2881 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2883 /* the included game controller base mappings should always be found */
2884 if (num_mappings == -1)
2885 Error(ERR_WARN, "no game controller base mappings found");
2888 Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2891 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2894 /* the personal game controller user mappings may or may not be found */
2895 if (num_mappings == -1)
2896 Error(ERR_WARN, "no game controller user mappings found");
2898 Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2900 Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2903 checked_free(mappings_file_base);
2904 checked_free(mappings_file_user);
2907 for (i = 0; i < SDL_NumJoysticks(); i++)
2909 const char *name, *type;
2911 if (SDL_IsGameController(i))
2913 name = SDL_GameControllerNameForIndex(i);
2914 type = "game controller";
2918 name = SDL_JoystickNameForIndex(i);
2922 Error(ERR_INFO, "- joystick %d (%s): '%s'",
2923 i, type, (name ? name : "(Unknown)"));
2929 /* assign joysticks from configured to connected joystick for all players */
2930 for (i = 0; i < MAX_PLAYERS; i++)
2932 /* get configured joystick for this player */
2933 char *device_name = setup.input[i].joy.device_name;
2934 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2936 if (joystick_nr >= SDL_NumJoysticks())
2938 if (setup.input[i].use_joystick && print_warning)
2939 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2944 /* store configured joystick number for each player */
2945 joystick.nr[i] = joystick_nr;
2948 /* now open all connected joysticks (regardless if configured or not) */
2949 for (i = 0; i < SDL_NumJoysticks(); i++)
2951 /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
2952 if (SDLCheckJoystickOpened(i))
2953 SDLCloseJoystick(i);
2955 if (SDLOpenJoystick(i))
2956 joystick.status = JOYSTICK_ACTIVATED;
2957 else if (print_warning)
2958 Error(ERR_WARN, "cannot open joystick %d", i);
2961 SDLClearJoystickState();
2964 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2966 if (nr < 0 || nr >= MAX_PLAYERS)
2970 *x = sdl_js_axis[nr][0];
2972 *y = sdl_js_axis[nr][1];
2975 *b1 = sdl_js_button[nr][0];
2977 *b2 = sdl_js_button[nr][1];
2983 /* ========================================================================= */
2984 /* touch input overlay functions */
2985 /* ========================================================================= */
2987 #if defined(USE_TOUCH_INPUT_OVERLAY)
2988 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2991 int grid_xsize = overlay.grid_xsize;
2992 int grid_ysize = overlay.grid_ysize;
2995 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2996 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2998 for (x = 0; x < grid_xsize; x++)
3000 rect.x = (x + 0) * video.screen_width / grid_xsize;
3001 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
3003 for (y = 0; y < grid_ysize; y++)
3005 rect.y = (y + 0) * video.screen_height / grid_ysize;
3006 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
3008 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
3009 SDL_RenderDrawRect(sdl_renderer, &rect);
3013 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3016 static void RenderFillRectangle(int x, int y, int width, int height)
3018 SDL_Rect rect = { x, y, width, height };
3020 SDL_RenderFillRect(sdl_renderer, &rect);
3023 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
3025 static int alpha_direction = 0;
3026 static int alpha_highlight = 0;
3027 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3028 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3030 int grid_xsize = overlay.grid_xsize;
3031 int grid_ysize = overlay.grid_ysize;
3034 if (alpha == alpha_max)
3036 if (alpha_direction < 0)
3038 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
3040 if (alpha_highlight == 0)
3041 alpha_direction = 1;
3045 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
3047 if (alpha_highlight == alpha_max)
3048 alpha_direction = -1;
3053 alpha_direction = 1;
3054 alpha_highlight = alpha;
3057 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
3059 for (x = 0; x < grid_xsize; x++)
3061 for (y = 0; y < grid_ysize; y++)
3063 int grid_button = overlay.grid_button[x][y];
3064 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
3065 int alpha_draw = alpha;
3066 int outline_border = MV_NONE;
3067 int border_size = 2;
3068 boolean draw_outlined = setup.touch.draw_outlined;
3069 boolean draw_pressed = setup.touch.draw_pressed;
3071 if (grid_button == CHAR_GRID_BUTTON_NONE)
3074 if (grid_button == overlay.grid_button_highlight)
3075 alpha_draw = alpha_highlight;
3077 if (draw_pressed && overlay.grid_button_action & grid_button_action)
3080 draw_outlined = FALSE;
3082 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
3085 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
3087 rect.x = (x + 0) * video.screen_width / grid_xsize;
3088 rect.y = (y + 0) * video.screen_height / grid_ysize;
3089 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
3090 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
3092 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
3094 rect.x += border_size;
3095 rect.w -= border_size;
3097 outline_border |= MV_LEFT;
3100 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
3102 rect.w -= border_size;
3104 outline_border |= MV_RIGHT;
3107 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
3109 rect.y += border_size;
3110 rect.h -= border_size;
3112 outline_border |= MV_UP;
3115 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
3117 rect.h -= border_size;
3119 outline_border |= MV_DOWN;
3124 int rect_x = rect.x +
3125 (outline_border & MV_LEFT ? border_size : 0);
3126 int rect_w = rect.w -
3127 (outline_border & MV_LEFT ? border_size : 0) -
3128 (outline_border & MV_RIGHT ? border_size : 0);
3130 if (outline_border & MV_LEFT)
3131 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
3133 if (outline_border & MV_RIGHT)
3134 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
3135 border_size, rect.h);
3137 if (outline_border & MV_UP)
3138 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
3140 if (outline_border & MV_DOWN)
3141 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
3142 rect_w, border_size);
3146 SDL_RenderFillRect(sdl_renderer, &rect);
3151 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3154 static void DrawTouchInputOverlay()
3156 static SDL_Texture *texture = NULL;
3157 static boolean initialized = FALSE;
3158 static boolean deactivated = TRUE;
3159 static boolean show_grid = FALSE;
3160 static int width = 0, height = 0;
3161 static int alpha_last = -1;
3162 static int alpha = 0;
3163 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3164 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3165 boolean active = (overlay.enabled && overlay.active);
3167 if (!active && deactivated)
3172 if (alpha < alpha_max)
3173 alpha = MIN(alpha + alpha_step, alpha_max);
3175 deactivated = FALSE;
3179 alpha = MAX(0, alpha - alpha_step);
3185 if (overlay.show_grid)
3187 else if (deactivated)
3191 DrawTouchInputOverlay_ShowGrid(alpha);
3193 DrawTouchInputOverlay_ShowGridButtons(alpha);
3198 // !!! VIRTUAL BUTTONS FROM IMAGE FILE NOT USED ANYMORE !!!
3202 char *basename = "overlay/VirtualButtons.png";
3203 char *filename = getCustomImageFilename(basename);
3205 if (filename == NULL)
3206 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
3208 SDL_Surface *surface;
3210 if ((surface = IMG_Load(filename)) == NULL)
3211 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
3214 height = surface->h;
3216 /* set black pixel to transparent if no alpha channel / transparent color */
3217 if (!SDLHasAlpha(surface) &&
3218 !SDLHasColorKey(surface))
3219 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
3220 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
3222 if ((texture = SDLCreateTextureFromSurface(surface)) == NULL)
3223 Error(ERR_EXIT, "SDLCreateTextureFromSurface() failed");
3225 SDL_FreeSurface(surface);
3227 SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
3232 if (alpha != alpha_last)
3233 SDL_SetTextureAlphaMod(texture, alpha);
3237 float ratio_overlay = (float) width / height;
3238 float ratio_screen = (float) video.screen_width / video.screen_height;
3239 int width_scaled, height_scaled;
3242 if (ratio_overlay > ratio_screen)
3244 width_scaled = video.screen_width;
3245 height_scaled = video.screen_height * ratio_screen / ratio_overlay;
3247 ypos = video.screen_height - height_scaled;
3251 width_scaled = video.screen_width * ratio_overlay / ratio_screen;
3252 height_scaled = video.screen_height;
3253 xpos = (video.screen_width - width_scaled) / 2;
3257 SDL_Rect src_rect = { 0, 0, width, height };
3258 SDL_Rect dst_rect = { xpos, ypos, width_scaled, height_scaled };
3260 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);