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(void)
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(void)
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(void)
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(void)
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(void)
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 static 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;
1573 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1574 Uint8 R, Uint8 G, Uint8 B)
1576 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1579 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1581 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1584 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1586 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1589 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1594 /* Gack - slow, but endian correct */
1595 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1596 shift = surface->format->Rshift;
1597 *(pix+shift/8) = color>>shift;
1598 shift = surface->format->Gshift;
1599 *(pix+shift/8) = color>>shift;
1600 shift = surface->format->Bshift;
1601 *(pix+shift/8) = color>>shift;
1604 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1606 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1609 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1611 switch (dest->format->BytesPerPixel)
1614 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1618 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1622 _PutPixel24(dest,x,y,color);
1626 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1632 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1634 if (SDL_MUSTLOCK(surface))
1636 if (SDL_LockSurface(surface) < 0)
1642 _PutPixel(surface, x, y, color);
1644 if (SDL_MUSTLOCK(surface))
1646 SDL_UnlockSurface(surface);
1651 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1652 Uint8 r, Uint8 g, Uint8 b)
1654 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1657 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1659 if (y >= 0 && y <= dest->h - 1)
1661 switch (dest->format->BytesPerPixel)
1664 return y*dest->pitch;
1668 return y*dest->pitch/2;
1672 return y*dest->pitch;
1676 return y*dest->pitch/4;
1684 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1687 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1689 switch (surface->format->BytesPerPixel)
1693 /* Assuming 8-bpp */
1694 *((Uint8 *)surface->pixels + ypitch + x) = color;
1700 /* Probably 15-bpp or 16-bpp */
1701 *((Uint16 *)surface->pixels + ypitch + x) = color;
1707 /* Slow 24-bpp mode, usually not used */
1711 /* Gack - slow, but endian correct */
1712 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1713 shift = surface->format->Rshift;
1714 *(pix+shift/8) = color>>shift;
1715 shift = surface->format->Gshift;
1716 *(pix+shift/8) = color>>shift;
1717 shift = surface->format->Bshift;
1718 *(pix+shift/8) = color>>shift;
1724 /* Probably 32-bpp */
1725 *((Uint32 *)surface->pixels + ypitch + x) = color;
1732 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1737 if (SDL_MUSTLOCK(Surface))
1739 if (SDL_LockSurface(Surface) < 0)
1752 /* Do the clipping */
1753 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1757 if (x2 > Surface->w - 1)
1758 x2 = Surface->w - 1;
1765 SDL_FillRect(Surface, &l, Color);
1767 if (SDL_MUSTLOCK(Surface))
1769 SDL_UnlockSurface(Surface);
1773 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1774 Uint8 R, Uint8 G, Uint8 B)
1776 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1779 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1791 /* Do the clipping */
1792 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1796 if (x2 > Surface->w - 1)
1797 x2 = Surface->w - 1;
1804 SDL_FillRect(Surface, &l, Color);
1807 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1812 if (SDL_MUSTLOCK(Surface))
1814 if (SDL_LockSurface(Surface) < 0)
1827 /* Do the clipping */
1828 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1832 if (y2 > Surface->h - 1)
1833 y2 = Surface->h - 1;
1840 SDL_FillRect(Surface, &l, Color);
1842 if (SDL_MUSTLOCK(Surface))
1844 SDL_UnlockSurface(Surface);
1848 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1849 Uint8 R, Uint8 G, Uint8 B)
1851 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1854 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1866 /* Do the clipping */
1867 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1871 if (y2 > Surface->h - 1)
1872 y2 = Surface->h - 1;
1879 SDL_FillRect(Surface, &l, Color);
1883 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1884 Sint16 x2, Sint16 y2, Uint32 Color,
1885 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1888 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1893 sdx = (dx < 0) ? -1 : 1;
1894 sdy = (dy < 0) ? -1 : 1;
1906 for (x = 0; x < dx; x++)
1908 Callback(Surface, px, py, Color);
1922 for (y = 0; y < dy; y++)
1924 Callback(Surface, px, py, Color);
1939 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1940 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1941 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1944 sge_DoLine(Surface, X1, Y1, X2, Y2,
1945 SDL_MapRGB(Surface->format, R, G, B), Callback);
1949 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1952 if (SDL_MUSTLOCK(Surface))
1954 if (SDL_LockSurface(Surface) < 0)
1959 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1961 /* unlock the display */
1962 if (SDL_MUSTLOCK(Surface))
1964 SDL_UnlockSurface(Surface);
1969 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1970 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1972 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1976 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1978 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1983 -----------------------------------------------------------------------------
1984 quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1985 -----------------------------------------------------------------------------
1988 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1989 int width, int height, Uint32 color)
1993 for (y = src_y; y < src_y + height; y++)
1995 for (x = src_x; x < src_x + width; x++)
1997 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1999 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
2004 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
2005 int src_x, int src_y, int width, int height,
2006 int dst_x, int dst_y)
2010 for (y = 0; y < height; y++)
2012 for (x = 0; x < width; x++)
2014 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
2016 if (pixel != BLACK_PIXEL)
2017 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
2023 /* ========================================================================= */
2024 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
2025 /* (Rotozoomer) by Andreas Schiffler */
2026 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html */
2027 /* ========================================================================= */
2030 -----------------------------------------------------------------------------
2033 zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
2034 -----------------------------------------------------------------------------
2045 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
2048 tColorRGBA *sp, *csp, *dp;
2052 sp = csp = (tColorRGBA *) src->pixels;
2053 dp = (tColorRGBA *) dst->pixels;
2054 dgap = dst->pitch - dst->w * 4;
2056 for (y = 0; y < dst->h; y++)
2060 for (x = 0; x < dst->w; x++)
2062 tColorRGBA *sp0 = sp;
2063 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
2064 tColorRGBA *sp00 = &sp0[0];
2065 tColorRGBA *sp01 = &sp0[1];
2066 tColorRGBA *sp10 = &sp1[0];
2067 tColorRGBA *sp11 = &sp1[1];
2070 /* create new color pixel from all four source color pixels */
2071 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
2072 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
2073 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
2074 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
2079 /* advance source pointers */
2082 /* advance destination pointer */
2086 /* advance source pointer */
2087 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2089 /* advance destination pointers */
2090 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2096 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2098 int x, y, *sax, *say, *csax, *csay;
2100 tColorRGBA *sp, *csp, *csp0, *dp;
2103 /* use specialized zoom function when scaling down to exactly half size */
2104 if (src->w == 2 * dst->w &&
2105 src->h == 2 * dst->h)
2106 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2108 /* variable setup */
2109 sx = (float) src->w / (float) dst->w;
2110 sy = (float) src->h / (float) dst->h;
2112 /* allocate memory for row increments */
2113 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2114 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2116 /* precalculate row increments */
2117 for (x = 0; x <= dst->w; x++)
2118 *csax++ = (int)(sx * x);
2120 for (y = 0; y <= dst->h; y++)
2121 *csay++ = (int)(sy * y);
2124 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2125 dp = (tColorRGBA *) dst->pixels;
2126 dgap = dst->pitch - dst->w * 4;
2129 for (y = 0; y < dst->h; y++)
2134 for (x = 0; x < dst->w; x++)
2139 /* advance source pointers */
2143 /* advance destination pointer */
2147 /* advance source pointer */
2149 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2151 /* advance destination pointers */
2152 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2162 -----------------------------------------------------------------------------
2165 zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2166 -----------------------------------------------------------------------------
2169 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2171 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2172 Uint8 *sp, *dp, *csp;
2175 /* variable setup */
2176 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2177 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2179 /* allocate memory for row increments */
2180 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2181 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2183 /* precalculate row increments */
2186 for (x = 0; x < dst->w; x++)
2189 *csax = (csx >> 16);
2196 for (y = 0; y < dst->h; y++)
2199 *csay = (csy >> 16);
2206 for (x = 0; x < dst->w; x++)
2214 for (y = 0; y < dst->h; y++)
2221 sp = csp = (Uint8 *) src->pixels;
2222 dp = (Uint8 *) dst->pixels;
2223 dgap = dst->pitch - dst->w;
2227 for (y = 0; y < dst->h; y++)
2231 for (x = 0; x < dst->w; x++)
2236 /* advance source pointers */
2240 /* advance destination pointer */
2244 /* advance source pointer (for row) */
2245 csp += ((*csay) * src->pitch);
2248 /* advance destination pointers */
2259 -----------------------------------------------------------------------------
2262 Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2263 'zoomx' and 'zoomy' are scaling factors for width and height.
2264 If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2265 into a 32bit RGBA format on the fly.
2266 -----------------------------------------------------------------------------
2269 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2271 SDL_Surface *zoom_src = NULL;
2272 SDL_Surface *zoom_dst = NULL;
2273 boolean is_converted = FALSE;
2280 /* determine if source surface is 32 bit or 8 bit */
2281 is_32bit = (src->format->BitsPerPixel == 32);
2283 if (is_32bit || src->format->BitsPerPixel == 8)
2285 /* use source surface 'as is' */
2290 /* new source surface is 32 bit with a defined RGB ordering */
2291 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2292 0x000000ff, 0x0000ff00, 0x00ff0000,
2293 (src->format->Amask ? 0xff000000 : 0));
2294 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2296 is_converted = TRUE;
2299 /* allocate surface to completely contain the zoomed surface */
2302 /* target surface is 32 bit with source RGBA/ABGR ordering */
2303 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2304 zoom_src->format->Rmask,
2305 zoom_src->format->Gmask,
2306 zoom_src->format->Bmask,
2307 zoom_src->format->Amask);
2311 /* target surface is 8 bit */
2312 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2316 /* lock source surface */
2317 SDL_LockSurface(zoom_src);
2319 /* check which kind of surface we have */
2322 /* call the 32 bit transformation routine to do the zooming */
2323 zoomSurfaceRGBA(zoom_src, zoom_dst);
2328 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2329 zoom_dst->format->palette->colors[i] =
2330 zoom_src->format->palette->colors[i];
2331 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2333 /* call the 8 bit transformation routine to do the zooming */
2334 zoomSurfaceY(zoom_src, zoom_dst);
2337 /* unlock source surface */
2338 SDL_UnlockSurface(zoom_src);
2340 /* free temporary surface */
2342 SDL_FreeSurface(zoom_src);
2344 /* return destination surface */
2348 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2350 SDL_Surface *new_surface;
2352 if (surface == NULL)
2355 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2356 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2358 /* remove alpha channel from native non-transparent surface, if defined */
2359 SDLSetAlpha(new_surface, FALSE, 0);
2361 /* remove transparent color from native non-transparent surface, if defined */
2362 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2367 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2369 Bitmap *dst_bitmap = CreateBitmapStruct();
2370 SDL_Surface *src_surface = src_bitmap->surface_masked;
2371 SDL_Surface *dst_surface;
2373 dst_width = MAX(1, dst_width); /* prevent zero bitmap width */
2374 dst_height = MAX(1, dst_height); /* prevent zero bitmap height */
2376 dst_bitmap->width = dst_width;
2377 dst_bitmap->height = dst_height;
2379 /* create zoomed temporary surface from source surface */
2380 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2382 /* create native format destination surface from zoomed temporary surface */
2383 SDLSetNativeSurface(&dst_surface);
2385 /* set color key for zoomed surface from source surface, if defined */
2386 if (SDLHasColorKey(src_surface))
2387 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2388 SDLGetColorKey(src_surface));
2390 /* create native non-transparent surface for opaque blitting */
2391 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2393 /* set native transparent surface for masked blitting */
2394 dst_bitmap->surface_masked = dst_surface;
2400 /* ========================================================================= */
2401 /* load image to bitmap */
2402 /* ========================================================================= */
2404 Bitmap *SDLLoadImage(char *filename)
2406 Bitmap *new_bitmap = CreateBitmapStruct();
2407 SDL_Surface *sdl_image_tmp;
2409 if (program.headless)
2411 /* prevent sanity check warnings at later stage */
2412 new_bitmap->width = new_bitmap->height = 1;
2417 print_timestamp_init("SDLLoadImage");
2419 print_timestamp_time(getBaseNamePtr(filename));
2421 /* load image to temporary surface */
2422 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2423 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
2425 print_timestamp_time("IMG_Load");
2427 UPDATE_BUSY_STATE();
2429 /* create native non-transparent surface for current image */
2430 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2431 Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2433 print_timestamp_time("SDLGetNativeSurface (opaque)");
2435 UPDATE_BUSY_STATE();
2437 /* set black pixel to transparent if no alpha channel / transparent color */
2438 if (!SDLHasAlpha(sdl_image_tmp) &&
2439 !SDLHasColorKey(sdl_image_tmp))
2440 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2441 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2443 /* create native transparent surface for current image */
2444 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2445 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2447 print_timestamp_time("SDLGetNativeSurface (masked)");
2449 UPDATE_BUSY_STATE();
2451 /* free temporary surface */
2452 SDL_FreeSurface(sdl_image_tmp);
2454 new_bitmap->width = new_bitmap->surface->w;
2455 new_bitmap->height = new_bitmap->surface->h;
2457 print_timestamp_done("SDLLoadImage");
2463 /* ------------------------------------------------------------------------- */
2464 /* custom cursor fuctions */
2465 /* ------------------------------------------------------------------------- */
2467 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2469 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2470 cursor_info->width, cursor_info->height,
2471 cursor_info->hot_x, cursor_info->hot_y);
2474 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2476 static struct MouseCursorInfo *last_cursor_info = NULL;
2477 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2478 static SDL_Cursor *cursor_default = NULL;
2479 static SDL_Cursor *cursor_current = NULL;
2481 /* if invoked for the first time, store the SDL default cursor */
2482 if (cursor_default == NULL)
2483 cursor_default = SDL_GetCursor();
2485 /* only create new cursor if cursor info (custom only) has changed */
2486 if (cursor_info != NULL && cursor_info != last_cursor_info)
2488 cursor_current = create_cursor(cursor_info);
2489 last_cursor_info = cursor_info;
2492 /* only set new cursor if cursor info (custom or NULL) has changed */
2493 if (cursor_info != last_cursor_info2)
2494 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2496 last_cursor_info2 = cursor_info;
2500 /* ========================================================================= */
2501 /* audio functions */
2502 /* ========================================================================= */
2504 void SDLOpenAudio(void)
2506 if (program.headless)
2509 #if !defined(TARGET_SDL2)
2510 if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
2511 SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
2514 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2516 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2520 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2521 AUDIO_NUM_CHANNELS_STEREO,
2522 setup.system.audio_fragment_size) < 0)
2524 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2528 audio.sound_available = TRUE;
2529 audio.music_available = TRUE;
2530 audio.loops_available = TRUE;
2531 audio.sound_enabled = TRUE;
2533 /* set number of available mixer channels */
2534 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2535 audio.music_channel = MUSIC_CHANNEL;
2536 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2538 Mixer_InitChannels();
2541 void SDLCloseAudio(void)
2544 Mix_HaltChannel(-1);
2547 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2551 /* ========================================================================= */
2552 /* event functions */
2553 /* ========================================================================= */
2555 void SDLWaitEvent(Event *event)
2557 SDL_WaitEvent(event);
2560 void SDLHandleWindowManagerEvent(Event *event)
2563 #if defined(PLATFORM_WIN32)
2564 // experimental drag and drop code
2566 SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
2567 SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
2569 #if defined(TARGET_SDL2)
2570 if (syswmmsg->msg.win.msg == WM_DROPFILES)
2572 if (syswmmsg->msg == WM_DROPFILES)
2575 #if defined(TARGET_SDL2)
2576 HDROP hdrop = (HDROP)syswmmsg->msg.win.wParam;
2578 HDROP hdrop = (HDROP)syswmmsg->wParam;
2582 printf("::: SDL_SYSWMEVENT:\n");
2584 num_files = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
2586 for (i = 0; i < num_files; i++)
2588 int buffer_len = DragQueryFile(hdrop, i, NULL, 0);
2589 char buffer[buffer_len + 1];
2591 DragQueryFile(hdrop, i, buffer, buffer_len + 1);
2593 printf("::: - '%s'\n", buffer);
2596 #if defined(TARGET_SDL2)
2597 DragFinish((HDROP)syswmmsg->msg.win.wParam);
2599 DragFinish((HDROP)syswmmsg->wParam);
2607 /* ========================================================================= */
2608 /* joystick functions */
2609 /* ========================================================================= */
2611 #if defined(TARGET_SDL2)
2612 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2614 static SDL_Joystick *sdl_joystick[MAX_PLAYERS]; // only joysticks supported
2616 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2617 static int sdl_js_axis[MAX_PLAYERS][2];
2618 static int sdl_js_button[MAX_PLAYERS][2];
2619 static boolean sdl_is_controller[MAX_PLAYERS];
2621 void SDLClearJoystickState(void)
2625 for (i = 0; i < MAX_PLAYERS; i++)
2627 for (j = 0; j < 2; j++)
2629 sdl_js_axis_raw[i][j] = -1;
2630 sdl_js_axis[i][j] = 0;
2631 sdl_js_button[i][j] = 0;
2636 boolean SDLOpenJoystick(int nr)
2638 if (nr < 0 || nr >= MAX_PLAYERS)
2641 #if defined(TARGET_SDL2)
2642 sdl_is_controller[nr] = SDL_IsGameController(nr);
2644 sdl_is_controller[nr] = FALSE;
2648 Error(ERR_DEBUG, "opening joystick %d (%s)",
2649 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2652 #if defined(TARGET_SDL2)
2653 if (sdl_is_controller[nr])
2654 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2656 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2658 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2661 return (sdl_joystick[nr] != NULL);
2664 void SDLCloseJoystick(int nr)
2666 if (nr < 0 || nr >= MAX_PLAYERS)
2670 Error(ERR_DEBUG, "closing joystick %d", nr);
2673 #if defined(TARGET_SDL2)
2674 if (sdl_is_controller[nr])
2675 SDL_GameControllerClose(sdl_joystick[nr]);
2677 SDL_JoystickClose(sdl_joystick[nr]);
2679 SDL_JoystickClose(sdl_joystick[nr]);
2682 sdl_joystick[nr] = NULL;
2685 boolean SDLCheckJoystickOpened(int nr)
2687 if (nr < 0 || nr >= MAX_PLAYERS)
2690 #if defined(TARGET_SDL2)
2691 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2693 return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
2697 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2699 #if defined(TARGET_SDL2)
2700 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2701 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2702 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2703 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2705 int axis_id = axis_id_raw % 2;
2708 if (nr < 0 || nr >= MAX_PLAYERS)
2714 // prevent (slightly jittering, but centered) axis A from resetting axis B
2715 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2716 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2719 sdl_js_axis[nr][axis_id] = axis_value;
2720 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2723 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2725 #if defined(TARGET_SDL2)
2726 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2727 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2728 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2729 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2730 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2731 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2732 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2733 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2736 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2737 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2738 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2739 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2740 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2741 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2742 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2743 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2745 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2746 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2747 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2748 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2749 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2751 int button_id = button_id_raw % 2;
2754 if (nr < 0 || nr >= MAX_PLAYERS)
2757 if (button_id == -1)
2760 sdl_js_button[nr][button_id] = button_state;
2763 void HandleJoystickEvent(Event *event)
2767 #if defined(TARGET_SDL2)
2768 case SDL_CONTROLLERDEVICEADDED:
2770 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2771 event->cdevice.which);
2776 case SDL_CONTROLLERDEVICEREMOVED:
2778 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2779 event->cdevice.which);
2784 case SDL_CONTROLLERAXISMOTION:
2786 Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2787 event->caxis.which, event->caxis.axis, event->caxis.value);
2789 setJoystickAxis(event->caxis.which,
2791 event->caxis.value);
2794 case SDL_CONTROLLERBUTTONDOWN:
2796 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2797 event->cbutton.which, event->cbutton.button);
2799 setJoystickButton(event->cbutton.which,
2800 event->cbutton.button,
2804 case SDL_CONTROLLERBUTTONUP:
2806 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2807 event->cbutton.which, event->cbutton.button);
2809 setJoystickButton(event->cbutton.which,
2810 event->cbutton.button,
2815 case SDL_JOYAXISMOTION:
2816 if (sdl_is_controller[event->jaxis.which])
2820 Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2821 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2823 if (event->jaxis.axis < 4)
2824 setJoystickAxis(event->jaxis.which,
2826 event->jaxis.value);
2829 case SDL_JOYBUTTONDOWN:
2830 if (sdl_is_controller[event->jaxis.which])
2834 Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2835 event->jbutton.which, event->jbutton.button);
2837 if (event->jbutton.button < 4)
2838 setJoystickButton(event->jbutton.which,
2839 event->jbutton.button,
2843 case SDL_JOYBUTTONUP:
2844 if (sdl_is_controller[event->jaxis.which])
2848 Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2849 event->jbutton.which, event->jbutton.button);
2851 if (event->jbutton.button < 4)
2852 setJoystickButton(event->jbutton.which,
2853 event->jbutton.button,
2862 void SDLInitJoysticks(void)
2864 static boolean sdl_joystick_subsystem_initialized = FALSE;
2865 boolean print_warning = !sdl_joystick_subsystem_initialized;
2866 #if defined(TARGET_SDL2)
2867 char *mappings_file_base = getPath2(options.conf_directory,
2868 GAMECONTROLLER_BASENAME);
2869 char *mappings_file_user = getPath2(getUserGameDataDir(),
2870 GAMECONTROLLER_BASENAME);
2875 if (!sdl_joystick_subsystem_initialized)
2877 sdl_joystick_subsystem_initialized = TRUE;
2879 #if defined(TARGET_SDL2)
2880 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2882 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2884 if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
2887 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2891 #if defined(TARGET_SDL2)
2892 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2894 /* the included game controller base mappings should always be found */
2895 if (num_mappings == -1)
2896 Error(ERR_WARN, "no game controller base mappings found");
2899 Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2902 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2905 /* the personal game controller user mappings may or may not be found */
2906 if (num_mappings == -1)
2907 Error(ERR_WARN, "no game controller user mappings found");
2909 Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2911 Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2914 checked_free(mappings_file_base);
2915 checked_free(mappings_file_user);
2918 for (i = 0; i < SDL_NumJoysticks(); i++)
2920 const char *name, *type;
2922 if (SDL_IsGameController(i))
2924 name = SDL_GameControllerNameForIndex(i);
2925 type = "game controller";
2929 name = SDL_JoystickNameForIndex(i);
2933 Error(ERR_INFO, "- joystick %d (%s): '%s'",
2934 i, type, (name ? name : "(Unknown)"));
2940 /* assign joysticks from configured to connected joystick for all players */
2941 for (i = 0; i < MAX_PLAYERS; i++)
2943 /* get configured joystick for this player */
2944 char *device_name = setup.input[i].joy.device_name;
2945 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2947 if (joystick_nr >= SDL_NumJoysticks())
2949 if (setup.input[i].use_joystick && print_warning)
2950 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2955 /* store configured joystick number for each player */
2956 joystick.nr[i] = joystick_nr;
2959 /* now open all connected joysticks (regardless if configured or not) */
2960 for (i = 0; i < SDL_NumJoysticks(); i++)
2962 /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
2963 if (SDLCheckJoystickOpened(i))
2964 SDLCloseJoystick(i);
2966 if (SDLOpenJoystick(i))
2967 joystick.status = JOYSTICK_ACTIVATED;
2968 else if (print_warning)
2969 Error(ERR_WARN, "cannot open joystick %d", i);
2972 SDLClearJoystickState();
2975 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2977 if (nr < 0 || nr >= MAX_PLAYERS)
2981 *x = sdl_js_axis[nr][0];
2983 *y = sdl_js_axis[nr][1];
2986 *b1 = sdl_js_button[nr][0];
2988 *b2 = sdl_js_button[nr][1];
2994 /* ========================================================================= */
2995 /* touch input overlay functions */
2996 /* ========================================================================= */
2998 #if defined(USE_TOUCH_INPUT_OVERLAY)
2999 static void DrawTouchInputOverlay_ShowGrid(int alpha)
3002 int grid_xsize = overlay.grid_xsize;
3003 int grid_ysize = overlay.grid_ysize;
3006 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
3007 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
3009 for (x = 0; x < grid_xsize; x++)
3011 rect.x = (x + 0) * video.screen_width / grid_xsize;
3012 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
3014 for (y = 0; y < grid_ysize; y++)
3016 rect.y = (y + 0) * video.screen_height / grid_ysize;
3017 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
3019 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
3020 SDL_RenderDrawRect(sdl_renderer, &rect);
3024 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3027 static void RenderFillRectangle(int x, int y, int width, int height)
3029 SDL_Rect rect = { x, y, width, height };
3031 SDL_RenderFillRect(sdl_renderer, &rect);
3034 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
3036 static int alpha_direction = 0;
3037 static int alpha_highlight = 0;
3038 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3039 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3041 int grid_xsize = overlay.grid_xsize;
3042 int grid_ysize = overlay.grid_ysize;
3045 if (alpha == alpha_max)
3047 if (alpha_direction < 0)
3049 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
3051 if (alpha_highlight == 0)
3052 alpha_direction = 1;
3056 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
3058 if (alpha_highlight == alpha_max)
3059 alpha_direction = -1;
3064 alpha_direction = 1;
3065 alpha_highlight = alpha;
3068 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
3070 for (x = 0; x < grid_xsize; x++)
3072 for (y = 0; y < grid_ysize; y++)
3074 int grid_button = overlay.grid_button[x][y];
3075 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
3076 int alpha_draw = alpha;
3077 int outline_border = MV_NONE;
3078 int border_size = 2;
3079 boolean draw_outlined = setup.touch.draw_outlined;
3080 boolean draw_pressed = setup.touch.draw_pressed;
3082 if (grid_button == CHAR_GRID_BUTTON_NONE)
3085 if (grid_button == overlay.grid_button_highlight)
3086 alpha_draw = alpha_highlight;
3088 if (draw_pressed && overlay.grid_button_action & grid_button_action)
3091 draw_outlined = FALSE;
3093 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
3096 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
3098 rect.x = (x + 0) * video.screen_width / grid_xsize;
3099 rect.y = (y + 0) * video.screen_height / grid_ysize;
3100 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
3101 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
3103 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
3105 rect.x += border_size;
3106 rect.w -= border_size;
3108 outline_border |= MV_LEFT;
3111 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
3113 rect.w -= border_size;
3115 outline_border |= MV_RIGHT;
3118 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
3120 rect.y += border_size;
3121 rect.h -= border_size;
3123 outline_border |= MV_UP;
3126 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
3128 rect.h -= border_size;
3130 outline_border |= MV_DOWN;
3135 int rect_x = rect.x +
3136 (outline_border & MV_LEFT ? border_size : 0);
3137 int rect_w = rect.w -
3138 (outline_border & MV_LEFT ? border_size : 0) -
3139 (outline_border & MV_RIGHT ? border_size : 0);
3141 if (outline_border & MV_LEFT)
3142 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
3144 if (outline_border & MV_RIGHT)
3145 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
3146 border_size, rect.h);
3148 if (outline_border & MV_UP)
3149 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
3151 if (outline_border & MV_DOWN)
3152 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
3153 rect_w, border_size);
3157 SDL_RenderFillRect(sdl_renderer, &rect);
3162 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3165 static void DrawTouchInputOverlay(void)
3167 static SDL_Texture *texture = NULL;
3168 static boolean initialized = FALSE;
3169 static boolean deactivated = TRUE;
3170 static boolean show_grid = FALSE;
3171 static int width = 0, height = 0;
3172 static int alpha_last = -1;
3173 static int alpha = 0;
3174 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3175 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3176 boolean active = (overlay.enabled && overlay.active);
3178 if (!active && deactivated)
3183 if (alpha < alpha_max)
3184 alpha = MIN(alpha + alpha_step, alpha_max);
3186 deactivated = FALSE;
3190 alpha = MAX(0, alpha - alpha_step);
3196 if (overlay.show_grid)
3198 else if (deactivated)
3202 DrawTouchInputOverlay_ShowGrid(alpha);
3204 DrawTouchInputOverlay_ShowGridButtons(alpha);
3209 // !!! VIRTUAL BUTTONS FROM IMAGE FILE NOT USED ANYMORE !!!
3213 char *basename = "overlay/VirtualButtons.png";
3214 char *filename = getCustomImageFilename(basename);
3216 if (filename == NULL)
3217 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
3219 SDL_Surface *surface;
3221 if ((surface = IMG_Load(filename)) == NULL)
3222 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
3225 height = surface->h;
3227 /* set black pixel to transparent if no alpha channel / transparent color */
3228 if (!SDLHasAlpha(surface) &&
3229 !SDLHasColorKey(surface))
3230 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
3231 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
3233 if ((texture = SDLCreateTextureFromSurface(surface)) == NULL)
3234 Error(ERR_EXIT, "SDLCreateTextureFromSurface() failed");
3236 SDL_FreeSurface(surface);
3238 SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
3243 if (alpha != alpha_last)
3244 SDL_SetTextureAlphaMod(texture, alpha);
3248 float ratio_overlay = (float) width / height;
3249 float ratio_screen = (float) video.screen_width / video.screen_height;
3250 int width_scaled, height_scaled;
3253 if (ratio_overlay > ratio_screen)
3255 width_scaled = video.screen_width;
3256 height_scaled = video.screen_height * ratio_screen / ratio_overlay;
3258 ypos = video.screen_height - height_scaled;
3262 width_scaled = video.screen_width * ratio_overlay / ratio_screen;
3263 height_scaled = video.screen_height;
3264 xpos = (video.screen_width - width_scaled) / 2;
3268 SDL_Rect src_rect = { 0, 0, width, height };
3269 SDL_Rect dst_rect = { xpos, ypos, width_scaled, height_scaled };
3271 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);