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 SDLSetScreenVsyncMode(setup.vsync_mode);
682 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
683 SDL_PIXELFORMAT_ARGB8888,
684 SDL_TEXTUREACCESS_STREAMING,
687 if (SDL_RenderTargetSupported(sdl_renderer))
688 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
689 SDL_PIXELFORMAT_ARGB8888,
690 SDL_TEXTUREACCESS_TARGET,
693 if (sdl_texture_stream != NULL)
695 // use SDL default values for RGB masks and no alpha channel
696 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
698 if (new_surface == NULL)
699 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
703 Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
708 Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
713 Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
718 if (gfx.final_screen_bitmap == NULL)
719 gfx.final_screen_bitmap = CreateBitmapStruct();
721 gfx.final_screen_bitmap->width = width;
722 gfx.final_screen_bitmap->height = height;
724 gfx.final_screen_bitmap->surface =
725 SDL_SetVideoMode(width, height, video.depth, surface_flags);
727 if (gfx.final_screen_bitmap->surface != NULL)
730 SDL_CreateRGBSurface(surface_flags, width, height, video.depth, 0,0,0, 0);
732 if (new_surface == NULL)
733 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
736 new_surface = gfx.final_screen_bitmap->surface;
737 gfx.final_screen_bitmap = NULL;
743 Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
747 #if defined(TARGET_SDL2)
748 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
749 if (new_surface != NULL)
750 fullscreen_enabled = fullscreen;
753 if (backbuffer == NULL)
754 backbuffer = CreateBitmapStruct();
756 backbuffer->width = video.width;
757 backbuffer->height = video.height;
759 if (backbuffer->surface)
760 SDL_FreeSurface(backbuffer->surface);
762 backbuffer->surface = new_surface;
764 return (new_surface != NULL);
767 boolean SDLSetVideoMode(boolean fullscreen)
769 boolean success = FALSE;
773 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
775 /* switch display to fullscreen mode, if available */
776 success = SDLCreateScreen(TRUE);
780 /* switching display to fullscreen mode failed -- do not try it again */
781 video.fullscreen_available = FALSE;
785 video.fullscreen_enabled = TRUE;
789 if ((!fullscreen && video.fullscreen_enabled) || !success)
791 /* switch display to window mode */
792 success = SDLCreateScreen(FALSE);
796 /* switching display to window mode failed -- should not happen */
800 video.fullscreen_enabled = FALSE;
801 video.window_scaling_percent = setup.window_scaling_percent;
802 video.window_scaling_quality = setup.window_scaling_quality;
804 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
808 #if defined(TARGET_SDL2)
809 SDLRedrawWindow(); // map window
813 #if defined(PLATFORM_WIN32)
814 // experimental drag and drop code
816 SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
819 SDL_SysWMinfo wminfo;
821 boolean wminfo_success = FALSE;
823 SDL_VERSION(&wminfo.version);
824 #if defined(TARGET_SDL2)
826 wminfo_success = SDL_GetWindowWMInfo(sdl_window, &wminfo);
828 wminfo_success = (SDL_GetWMInfo(&wminfo) == 1);
833 #if defined(TARGET_SDL2)
834 hwnd = wminfo.info.win.window;
836 hwnd = wminfo.window;
839 DragAcceptFiles(hwnd, TRUE);
848 void SDLSetWindowTitle(void)
850 #if defined(TARGET_SDL2)
851 if (sdl_window == NULL)
854 SDL_SetWindowTitle(sdl_window, program.window_title);
856 SDL_WM_SetCaption(program.window_title, program.window_title);
860 #if defined(TARGET_SDL2)
861 void SDLSetWindowScaling(int window_scaling_percent)
863 if (sdl_window == NULL)
866 float window_scaling_factor = (float)window_scaling_percent / 100;
867 int new_window_width = (int)(window_scaling_factor * video.screen_width);
868 int new_window_height = (int)(window_scaling_factor * video.screen_height);
870 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
872 video.window_scaling_percent = window_scaling_percent;
873 video.window_width = new_window_width;
874 video.window_height = new_window_height;
879 void SDLSetWindowScalingQuality(char *window_scaling_quality)
881 SDL_Texture *new_texture;
883 if (sdl_texture_stream == NULL)
886 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
888 new_texture = SDL_CreateTexture(sdl_renderer,
889 SDL_PIXELFORMAT_ARGB8888,
890 SDL_TEXTUREACCESS_STREAMING,
891 video.width, video.height);
893 if (new_texture != NULL)
895 SDL_DestroyTexture(sdl_texture_stream);
897 sdl_texture_stream = new_texture;
900 if (SDL_RenderTargetSupported(sdl_renderer))
901 new_texture = SDL_CreateTexture(sdl_renderer,
902 SDL_PIXELFORMAT_ARGB8888,
903 SDL_TEXTUREACCESS_TARGET,
904 video.width, video.height);
908 if (new_texture != NULL)
910 SDL_DestroyTexture(sdl_texture_target);
912 sdl_texture_target = new_texture;
917 video.window_scaling_quality = window_scaling_quality;
920 void SDLSetWindowFullscreen(boolean fullscreen)
922 if (sdl_window == NULL)
925 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
927 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
928 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
930 // if screen size was changed in fullscreen mode, correct desktop window size
931 if (!fullscreen && video.fullscreen_initial)
933 SDLSetWindowScaling(setup.window_scaling_percent);
934 SDL_SetWindowPosition(sdl_window,
935 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
937 video.fullscreen_initial = FALSE;
941 void SDLSetDisplaySize(void)
943 SDL_Rect display_bounds;
945 SDL_GetDisplayBounds(0, &display_bounds);
947 video.display_width = display_bounds.w;
948 video.display_height = display_bounds.h;
951 Error(ERR_DEBUG, "SDL real screen size: %d x %d",
952 video.display_width, video.display_height);
956 void SDLSetScreenSizeAndOffsets(int width, int height)
958 // set default video screen size and offsets
959 video.screen_width = width;
960 video.screen_height = height;
961 video.screen_xoffset = 0;
962 video.screen_yoffset = 0;
964 #if defined(USE_COMPLETE_DISPLAY)
965 float ratio_video = (float) width / height;
966 float ratio_display = (float) video.display_width / video.display_height;
968 if (ratio_video != ratio_display)
970 // adjust drawable screen size to cover the whole device display
972 if (ratio_video < ratio_display)
973 video.screen_width *= ratio_display / ratio_video;
975 video.screen_height *= ratio_video / ratio_display;
977 video.screen_xoffset = (video.screen_width - width) / 2;
978 video.screen_yoffset = (video.screen_height - height) / 2;
981 Error(ERR_DEBUG, "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
983 video.screen_width, video.screen_height,
984 ratio_video, ratio_display);
990 void SDLSetScreenSizeForRenderer(int width, int height)
992 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
995 void SDLSetScreenProperties(void)
997 SDLSetScreenSizeAndOffsets(video.width, video.height);
998 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
1003 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
1005 #if defined(TARGET_SDL2)
1006 video.screen_rendering_mode =
1007 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
1008 SPECIAL_RENDERING_BITMAP :
1009 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
1010 SPECIAL_RENDERING_TARGET:
1011 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
1012 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
1014 video.screen_rendering_mode = SPECIAL_RENDERING_BITMAP;
1018 void SDLSetScreenVsyncMode(char *vsync_mode)
1020 #if defined(TARGET_SDL2)
1022 (strEqual(vsync_mode, STR_VSYNC_MODE_NORMAL) ? VSYNC_MODE_NORMAL :
1023 strEqual(vsync_mode, STR_VSYNC_MODE_ADAPTIVE) ? VSYNC_MODE_ADAPTIVE :
1025 int result = SDL_GL_SetSwapInterval(interval);
1027 // if adaptive vsync requested, but not supported, retry with normal vsync
1028 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
1029 SDL_GL_SetSwapInterval(VSYNC_MODE_NORMAL);
1033 void SDLRedrawWindow(void)
1035 UpdateScreen_WithoutFrameDelay(NULL);
1038 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
1041 if (program.headless)
1044 SDL_Surface *surface =
1045 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
1047 if (surface == NULL)
1048 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
1050 SDLSetNativeSurface(&surface);
1052 bitmap->surface = surface;
1055 void SDLFreeBitmapPointers(Bitmap *bitmap)
1057 if (bitmap->surface)
1058 SDL_FreeSurface(bitmap->surface);
1059 if (bitmap->surface_masked)
1060 SDL_FreeSurface(bitmap->surface_masked);
1062 bitmap->surface = NULL;
1063 bitmap->surface_masked = NULL;
1065 #if defined(TARGET_SDL2)
1066 if (bitmap->texture)
1067 SDL_DestroyTexture(bitmap->texture);
1068 if (bitmap->texture_masked)
1069 SDL_DestroyTexture(bitmap->texture_masked);
1071 bitmap->texture = NULL;
1072 bitmap->texture_masked = NULL;
1076 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1077 int src_x, int src_y, int width, int height,
1078 int dst_x, int dst_y, int mask_mode)
1080 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1081 SDL_Rect src_rect, dst_rect;
1086 src_rect.h = height;
1091 dst_rect.h = height;
1093 // if (src_bitmap != backbuffer || dst_bitmap != window)
1094 if (!(src_bitmap == backbuffer && dst_bitmap == window))
1095 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
1096 src_bitmap->surface_masked : src_bitmap->surface),
1097 &src_rect, real_dst_bitmap->surface, &dst_rect);
1099 if (dst_bitmap == window)
1100 UpdateScreen_WithFrameDelay(&dst_rect);
1103 void SDLBlitTexture(Bitmap *bitmap,
1104 int src_x, int src_y, int width, int height,
1105 int dst_x, int dst_y, int mask_mode)
1107 #if defined(TARGET_SDL2)
1108 SDL_Texture *texture;
1113 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1115 if (texture == NULL)
1121 src_rect.h = height;
1126 dst_rect.h = height;
1128 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1132 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1135 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1143 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1145 if (dst_bitmap == window)
1146 UpdateScreen_WithFrameDelay(&rect);
1149 void PrepareFadeBitmap(int draw_target)
1151 Bitmap *fade_bitmap =
1152 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1153 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1155 if (fade_bitmap == NULL)
1158 // copy backbuffer to fading buffer
1159 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1161 // add border and animations to fading buffer
1162 FinalizeScreen(draw_target);
1165 void SDLFadeRectangle(int x, int y, int width, int height,
1166 int fade_mode, int fade_delay, int post_delay,
1167 void (*draw_border_function)(void))
1169 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1170 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1171 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1172 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1173 SDL_Surface *surface_screen = backbuffer->surface;
1174 SDL_Rect src_rect, dst_rect;
1176 int src_x = x, src_y = y;
1177 int dst_x = x, dst_y = y;
1178 unsigned int time_last, time_current;
1180 // store function for drawing global masked border
1181 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1183 // deactivate drawing of global border while fading, if needed
1184 if (draw_border_function == NULL)
1185 gfx.draw_global_border_function = NULL;
1190 src_rect.h = height;
1194 dst_rect.w = width; /* (ignored) */
1195 dst_rect.h = height; /* (ignored) */
1197 dst_rect2 = dst_rect;
1199 // before fading in, store backbuffer (without animation graphics)
1200 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1201 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1203 /* copy source and target surfaces to temporary surfaces for fading */
1204 if (fade_mode & FADE_TYPE_TRANSFORM)
1206 // (source and target fading buffer already prepared)
1208 else if (fade_mode & FADE_TYPE_FADE_IN)
1210 // (target fading buffer already prepared)
1211 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1213 else /* FADE_TYPE_FADE_OUT */
1215 // (source fading buffer already prepared)
1216 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1219 time_current = SDL_GetTicks();
1221 if (fade_mode == FADE_MODE_MELT)
1223 boolean done = FALSE;
1224 int melt_pixels = 2;
1225 int melt_columns = width / melt_pixels;
1226 int ypos[melt_columns];
1227 int max_steps = height / 8 + 32;
1232 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1234 SDLSetAlpha(surface_target, FALSE, 0); /* disable alpha blending */
1236 ypos[0] = -GetSimpleRandom(16);
1238 for (i = 1 ; i < melt_columns; i++)
1240 int r = GetSimpleRandom(3) - 1; /* randomly choose from { -1, 0, -1 } */
1242 ypos[i] = ypos[i - 1] + r;
1255 time_last = time_current;
1256 time_current = SDL_GetTicks();
1257 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1258 steps_final = MIN(MAX(0, steps), max_steps);
1262 done = (steps_done >= steps_final);
1264 for (i = 0 ; i < melt_columns; i++)
1272 else if (ypos[i] < height)
1277 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1279 if (ypos[i] + dy >= height)
1280 dy = height - ypos[i];
1282 /* copy part of (appearing) target surface to upper area */
1283 src_rect.x = src_x + i * melt_pixels;
1284 // src_rect.y = src_y + ypos[i];
1286 src_rect.w = melt_pixels;
1288 src_rect.h = ypos[i] + dy;
1290 dst_rect.x = dst_x + i * melt_pixels;
1291 // dst_rect.y = dst_y + ypos[i];
1294 if (steps_done >= steps_final)
1295 SDL_BlitSurface(surface_target, &src_rect,
1296 surface_screen, &dst_rect);
1300 /* copy part of (disappearing) source surface to lower area */
1301 src_rect.x = src_x + i * melt_pixels;
1303 src_rect.w = melt_pixels;
1304 src_rect.h = height - ypos[i];
1306 dst_rect.x = dst_x + i * melt_pixels;
1307 dst_rect.y = dst_y + ypos[i];
1309 if (steps_done >= steps_final)
1310 SDL_BlitSurface(surface_source, &src_rect,
1311 surface_screen, &dst_rect);
1317 src_rect.x = src_x + i * melt_pixels;
1319 src_rect.w = melt_pixels;
1320 src_rect.h = height;
1322 dst_rect.x = dst_x + i * melt_pixels;
1325 if (steps_done >= steps_final)
1326 SDL_BlitSurface(surface_target, &src_rect,
1327 surface_screen, &dst_rect);
1331 if (steps_done >= steps_final)
1333 if (draw_border_function != NULL)
1334 draw_border_function();
1336 UpdateScreen_WithFrameDelay(&dst_rect2);
1340 else if (fade_mode == FADE_MODE_CURTAIN)
1344 int xx_size = width / 2;
1346 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1348 SDLSetAlpha(surface_source, FALSE, 0); /* disable alpha blending */
1350 for (xx = 0; xx < xx_size;)
1352 time_last = time_current;
1353 time_current = SDL_GetTicks();
1354 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1355 xx_final = MIN(MAX(0, xx), xx_size);
1360 src_rect.h = height;
1365 /* draw new (target) image to screen buffer */
1366 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1368 if (xx_final < xx_size)
1370 src_rect.w = xx_size - xx_final;
1371 src_rect.h = height;
1373 /* draw old (source) image to screen buffer (left side) */
1375 src_rect.x = src_x + xx_final;
1378 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1380 /* draw old (source) image to screen buffer (right side) */
1382 src_rect.x = src_x + xx_size;
1383 dst_rect.x = dst_x + xx_size + xx_final;
1385 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1388 if (draw_border_function != NULL)
1389 draw_border_function();
1391 /* only update the region of the screen that is affected from fading */
1392 UpdateScreen_WithFrameDelay(&dst_rect2);
1395 else /* fading in, fading out or cross-fading */
1400 for (alpha = 0.0; alpha < 255.0;)
1402 time_last = time_current;
1403 time_current = SDL_GetTicks();
1404 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1405 alpha_final = MIN(MAX(0, alpha), 255);
1407 /* draw existing (source) image to screen buffer */
1408 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1410 /* draw new (target) image to screen buffer using alpha blending */
1411 SDLSetAlpha(surface_target, TRUE, alpha_final);
1412 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1414 if (draw_border_function != NULL)
1415 draw_border_function();
1417 /* only update the region of the screen that is affected from fading */
1418 UpdateScreen_WithFrameDelay(&dst_rect);
1423 Delay_WithScreenUpdates(post_delay);
1425 // restore function for drawing global masked border
1426 gfx.draw_global_border_function = draw_global_border_function;
1428 // after fading in, restore backbuffer (without animation graphics)
1429 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1430 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1433 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1434 int to_x, int to_y, Uint32 color)
1436 SDL_Surface *surface = dst_bitmap->surface;
1440 swap_numbers(&from_x, &to_x);
1443 swap_numbers(&from_y, &to_y);
1447 rect.w = (to_x - from_x + 1);
1448 rect.h = (to_y - from_y + 1);
1450 SDL_FillRect(surface, &rect, color);
1453 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1454 int to_x, int to_y, Uint32 color)
1456 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1459 #if ENABLE_UNUSED_CODE
1460 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1461 int num_points, Uint32 color)
1466 for (i = 0; i < num_points - 1; i++)
1468 for (x = 0; x < line_width; x++)
1470 for (y = 0; y < line_width; y++)
1472 int dx = x - line_width / 2;
1473 int dy = y - line_width / 2;
1475 if ((x == 0 && y == 0) ||
1476 (x == 0 && y == line_width - 1) ||
1477 (x == line_width - 1 && y == 0) ||
1478 (x == line_width - 1 && y == line_width - 1))
1481 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1482 points[i+1].x + dx, points[i+1].y + dy, color);
1489 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1491 SDL_Surface *surface = src_bitmap->surface;
1493 switch (surface->format->BytesPerPixel)
1495 case 1: /* assuming 8-bpp */
1497 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1501 case 2: /* probably 15-bpp or 16-bpp */
1503 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1507 case 3: /* slow 24-bpp mode; usually not used */
1509 /* does this work? */
1510 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1514 shift = surface->format->Rshift;
1515 color |= *(pix + shift / 8) >> shift;
1516 shift = surface->format->Gshift;
1517 color |= *(pix + shift / 8) >> shift;
1518 shift = surface->format->Bshift;
1519 color |= *(pix + shift / 8) >> shift;
1525 case 4: /* probably 32-bpp */
1527 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1536 /* ========================================================================= */
1537 /* The following functions were taken from the SGE library */
1538 /* (SDL Graphics Extension Library) by Anders Lindström */
1539 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html */
1540 /* ========================================================================= */
1542 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1544 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1546 switch (surface->format->BytesPerPixel)
1550 /* Assuming 8-bpp */
1551 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1557 /* Probably 15-bpp or 16-bpp */
1558 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1564 /* Slow 24-bpp mode, usually not used */
1568 /* Gack - slow, but endian correct */
1569 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1570 shift = surface->format->Rshift;
1571 *(pix+shift/8) = color>>shift;
1572 shift = surface->format->Gshift;
1573 *(pix+shift/8) = color>>shift;
1574 shift = surface->format->Bshift;
1575 *(pix+shift/8) = color>>shift;
1581 /* Probably 32-bpp */
1582 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1590 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1591 Uint8 R, Uint8 G, Uint8 B)
1593 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1596 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1598 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1601 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1603 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1606 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1611 /* Gack - slow, but endian correct */
1612 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1613 shift = surface->format->Rshift;
1614 *(pix+shift/8) = color>>shift;
1615 shift = surface->format->Gshift;
1616 *(pix+shift/8) = color>>shift;
1617 shift = surface->format->Bshift;
1618 *(pix+shift/8) = color>>shift;
1621 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1623 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1626 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1628 switch (dest->format->BytesPerPixel)
1631 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1635 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1639 _PutPixel24(dest,x,y,color);
1643 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1649 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1651 if (SDL_MUSTLOCK(surface))
1653 if (SDL_LockSurface(surface) < 0)
1659 _PutPixel(surface, x, y, color);
1661 if (SDL_MUSTLOCK(surface))
1663 SDL_UnlockSurface(surface);
1668 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1669 Uint8 r, Uint8 g, Uint8 b)
1671 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1674 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1676 if (y >= 0 && y <= dest->h - 1)
1678 switch (dest->format->BytesPerPixel)
1681 return y*dest->pitch;
1685 return y*dest->pitch/2;
1689 return y*dest->pitch;
1693 return y*dest->pitch/4;
1701 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1704 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1706 switch (surface->format->BytesPerPixel)
1710 /* Assuming 8-bpp */
1711 *((Uint8 *)surface->pixels + ypitch + x) = color;
1717 /* Probably 15-bpp or 16-bpp */
1718 *((Uint16 *)surface->pixels + ypitch + x) = color;
1724 /* Slow 24-bpp mode, usually not used */
1728 /* Gack - slow, but endian correct */
1729 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1730 shift = surface->format->Rshift;
1731 *(pix+shift/8) = color>>shift;
1732 shift = surface->format->Gshift;
1733 *(pix+shift/8) = color>>shift;
1734 shift = surface->format->Bshift;
1735 *(pix+shift/8) = color>>shift;
1741 /* Probably 32-bpp */
1742 *((Uint32 *)surface->pixels + ypitch + x) = color;
1749 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1754 if (SDL_MUSTLOCK(Surface))
1756 if (SDL_LockSurface(Surface) < 0)
1769 /* Do the clipping */
1770 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1774 if (x2 > Surface->w - 1)
1775 x2 = Surface->w - 1;
1782 SDL_FillRect(Surface, &l, Color);
1784 if (SDL_MUSTLOCK(Surface))
1786 SDL_UnlockSurface(Surface);
1790 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1791 Uint8 R, Uint8 G, Uint8 B)
1793 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1796 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1808 /* Do the clipping */
1809 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1813 if (x2 > Surface->w - 1)
1814 x2 = Surface->w - 1;
1821 SDL_FillRect(Surface, &l, Color);
1824 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1829 if (SDL_MUSTLOCK(Surface))
1831 if (SDL_LockSurface(Surface) < 0)
1844 /* Do the clipping */
1845 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1849 if (y2 > Surface->h - 1)
1850 y2 = Surface->h - 1;
1857 SDL_FillRect(Surface, &l, Color);
1859 if (SDL_MUSTLOCK(Surface))
1861 SDL_UnlockSurface(Surface);
1865 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1866 Uint8 R, Uint8 G, Uint8 B)
1868 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1871 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1883 /* Do the clipping */
1884 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1888 if (y2 > Surface->h - 1)
1889 y2 = Surface->h - 1;
1896 SDL_FillRect(Surface, &l, Color);
1900 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1901 Sint16 x2, Sint16 y2, Uint32 Color,
1902 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1905 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1910 sdx = (dx < 0) ? -1 : 1;
1911 sdy = (dy < 0) ? -1 : 1;
1923 for (x = 0; x < dx; x++)
1925 Callback(Surface, px, py, Color);
1939 for (y = 0; y < dy; y++)
1941 Callback(Surface, px, py, Color);
1956 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1957 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1958 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1961 sge_DoLine(Surface, X1, Y1, X2, Y2,
1962 SDL_MapRGB(Surface->format, R, G, B), Callback);
1966 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1969 if (SDL_MUSTLOCK(Surface))
1971 if (SDL_LockSurface(Surface) < 0)
1976 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1978 /* unlock the display */
1979 if (SDL_MUSTLOCK(Surface))
1981 SDL_UnlockSurface(Surface);
1986 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1987 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1989 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1993 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1995 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
2000 -----------------------------------------------------------------------------
2001 quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
2002 -----------------------------------------------------------------------------
2005 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
2006 int width, int height, Uint32 color)
2010 for (y = src_y; y < src_y + height; y++)
2012 for (x = src_x; x < src_x + width; x++)
2014 Uint32 pixel = SDLGetPixel(bitmap, x, y);
2016 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
2021 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
2022 int src_x, int src_y, int width, int height,
2023 int dst_x, int dst_y)
2027 for (y = 0; y < height; y++)
2029 for (x = 0; x < width; x++)
2031 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
2033 if (pixel != BLACK_PIXEL)
2034 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
2040 /* ========================================================================= */
2041 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
2042 /* (Rotozoomer) by Andreas Schiffler */
2043 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html */
2044 /* ========================================================================= */
2047 -----------------------------------------------------------------------------
2050 zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
2051 -----------------------------------------------------------------------------
2062 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
2065 tColorRGBA *sp, *csp, *dp;
2069 sp = csp = (tColorRGBA *) src->pixels;
2070 dp = (tColorRGBA *) dst->pixels;
2071 dgap = dst->pitch - dst->w * 4;
2073 for (y = 0; y < dst->h; y++)
2077 for (x = 0; x < dst->w; x++)
2079 tColorRGBA *sp0 = sp;
2080 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
2081 tColorRGBA *sp00 = &sp0[0];
2082 tColorRGBA *sp01 = &sp0[1];
2083 tColorRGBA *sp10 = &sp1[0];
2084 tColorRGBA *sp11 = &sp1[1];
2087 /* create new color pixel from all four source color pixels */
2088 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
2089 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
2090 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
2091 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
2096 /* advance source pointers */
2099 /* advance destination pointer */
2103 /* advance source pointer */
2104 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2106 /* advance destination pointers */
2107 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2113 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2115 int x, y, *sax, *say, *csax, *csay;
2117 tColorRGBA *sp, *csp, *csp0, *dp;
2120 /* use specialized zoom function when scaling down to exactly half size */
2121 if (src->w == 2 * dst->w &&
2122 src->h == 2 * dst->h)
2123 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2125 /* variable setup */
2126 sx = (float) src->w / (float) dst->w;
2127 sy = (float) src->h / (float) dst->h;
2129 /* allocate memory for row increments */
2130 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2131 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2133 /* precalculate row increments */
2134 for (x = 0; x <= dst->w; x++)
2135 *csax++ = (int)(sx * x);
2137 for (y = 0; y <= dst->h; y++)
2138 *csay++ = (int)(sy * y);
2141 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2142 dp = (tColorRGBA *) dst->pixels;
2143 dgap = dst->pitch - dst->w * 4;
2146 for (y = 0; y < dst->h; y++)
2151 for (x = 0; x < dst->w; x++)
2156 /* advance source pointers */
2160 /* advance destination pointer */
2164 /* advance source pointer */
2166 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2168 /* advance destination pointers */
2169 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2179 -----------------------------------------------------------------------------
2182 zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2183 -----------------------------------------------------------------------------
2186 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2188 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2189 Uint8 *sp, *dp, *csp;
2192 /* variable setup */
2193 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2194 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2196 /* allocate memory for row increments */
2197 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2198 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2200 /* precalculate row increments */
2203 for (x = 0; x < dst->w; x++)
2206 *csax = (csx >> 16);
2213 for (y = 0; y < dst->h; y++)
2216 *csay = (csy >> 16);
2223 for (x = 0; x < dst->w; x++)
2231 for (y = 0; y < dst->h; y++)
2238 sp = csp = (Uint8 *) src->pixels;
2239 dp = (Uint8 *) dst->pixels;
2240 dgap = dst->pitch - dst->w;
2244 for (y = 0; y < dst->h; y++)
2248 for (x = 0; x < dst->w; x++)
2253 /* advance source pointers */
2257 /* advance destination pointer */
2261 /* advance source pointer (for row) */
2262 csp += ((*csay) * src->pitch);
2265 /* advance destination pointers */
2276 -----------------------------------------------------------------------------
2279 Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2280 'zoomx' and 'zoomy' are scaling factors for width and height.
2281 If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2282 into a 32bit RGBA format on the fly.
2283 -----------------------------------------------------------------------------
2286 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2288 SDL_Surface *zoom_src = NULL;
2289 SDL_Surface *zoom_dst = NULL;
2290 boolean is_converted = FALSE;
2297 /* determine if source surface is 32 bit or 8 bit */
2298 is_32bit = (src->format->BitsPerPixel == 32);
2300 if (is_32bit || src->format->BitsPerPixel == 8)
2302 /* use source surface 'as is' */
2307 /* new source surface is 32 bit with a defined RGB ordering */
2308 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2309 0x000000ff, 0x0000ff00, 0x00ff0000,
2310 (src->format->Amask ? 0xff000000 : 0));
2311 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2313 is_converted = TRUE;
2316 /* allocate surface to completely contain the zoomed surface */
2319 /* target surface is 32 bit with source RGBA/ABGR ordering */
2320 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2321 zoom_src->format->Rmask,
2322 zoom_src->format->Gmask,
2323 zoom_src->format->Bmask,
2324 zoom_src->format->Amask);
2328 /* target surface is 8 bit */
2329 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2333 /* lock source surface */
2334 SDL_LockSurface(zoom_src);
2336 /* check which kind of surface we have */
2339 /* call the 32 bit transformation routine to do the zooming */
2340 zoomSurfaceRGBA(zoom_src, zoom_dst);
2345 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2346 zoom_dst->format->palette->colors[i] =
2347 zoom_src->format->palette->colors[i];
2348 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2350 /* call the 8 bit transformation routine to do the zooming */
2351 zoomSurfaceY(zoom_src, zoom_dst);
2354 /* unlock source surface */
2355 SDL_UnlockSurface(zoom_src);
2357 /* free temporary surface */
2359 SDL_FreeSurface(zoom_src);
2361 /* return destination surface */
2365 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2367 SDL_Surface *new_surface;
2369 if (surface == NULL)
2372 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2373 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2375 /* remove alpha channel from native non-transparent surface, if defined */
2376 SDLSetAlpha(new_surface, FALSE, 0);
2378 /* remove transparent color from native non-transparent surface, if defined */
2379 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2384 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2386 Bitmap *dst_bitmap = CreateBitmapStruct();
2387 SDL_Surface *src_surface = src_bitmap->surface_masked;
2388 SDL_Surface *dst_surface;
2390 dst_width = MAX(1, dst_width); /* prevent zero bitmap width */
2391 dst_height = MAX(1, dst_height); /* prevent zero bitmap height */
2393 dst_bitmap->width = dst_width;
2394 dst_bitmap->height = dst_height;
2396 /* create zoomed temporary surface from source surface */
2397 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2399 /* create native format destination surface from zoomed temporary surface */
2400 SDLSetNativeSurface(&dst_surface);
2402 /* set color key for zoomed surface from source surface, if defined */
2403 if (SDLHasColorKey(src_surface))
2404 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2405 SDLGetColorKey(src_surface));
2407 /* create native non-transparent surface for opaque blitting */
2408 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2410 /* set native transparent surface for masked blitting */
2411 dst_bitmap->surface_masked = dst_surface;
2417 /* ========================================================================= */
2418 /* load image to bitmap */
2419 /* ========================================================================= */
2421 Bitmap *SDLLoadImage(char *filename)
2423 Bitmap *new_bitmap = CreateBitmapStruct();
2424 SDL_Surface *sdl_image_tmp;
2426 if (program.headless)
2428 /* prevent sanity check warnings at later stage */
2429 new_bitmap->width = new_bitmap->height = 1;
2434 print_timestamp_init("SDLLoadImage");
2436 print_timestamp_time(getBaseNamePtr(filename));
2438 /* load image to temporary surface */
2439 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2440 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
2442 print_timestamp_time("IMG_Load");
2444 UPDATE_BUSY_STATE();
2446 /* create native non-transparent surface for current image */
2447 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2448 Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2450 print_timestamp_time("SDLGetNativeSurface (opaque)");
2452 UPDATE_BUSY_STATE();
2454 /* set black pixel to transparent if no alpha channel / transparent color */
2455 if (!SDLHasAlpha(sdl_image_tmp) &&
2456 !SDLHasColorKey(sdl_image_tmp))
2457 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2458 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2460 /* create native transparent surface for current image */
2461 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2462 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2464 print_timestamp_time("SDLGetNativeSurface (masked)");
2466 UPDATE_BUSY_STATE();
2468 /* free temporary surface */
2469 SDL_FreeSurface(sdl_image_tmp);
2471 new_bitmap->width = new_bitmap->surface->w;
2472 new_bitmap->height = new_bitmap->surface->h;
2474 print_timestamp_done("SDLLoadImage");
2480 /* ------------------------------------------------------------------------- */
2481 /* custom cursor fuctions */
2482 /* ------------------------------------------------------------------------- */
2484 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2486 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2487 cursor_info->width, cursor_info->height,
2488 cursor_info->hot_x, cursor_info->hot_y);
2491 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2493 static struct MouseCursorInfo *last_cursor_info = NULL;
2494 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2495 static SDL_Cursor *cursor_default = NULL;
2496 static SDL_Cursor *cursor_current = NULL;
2498 /* if invoked for the first time, store the SDL default cursor */
2499 if (cursor_default == NULL)
2500 cursor_default = SDL_GetCursor();
2502 /* only create new cursor if cursor info (custom only) has changed */
2503 if (cursor_info != NULL && cursor_info != last_cursor_info)
2505 cursor_current = create_cursor(cursor_info);
2506 last_cursor_info = cursor_info;
2509 /* only set new cursor if cursor info (custom or NULL) has changed */
2510 if (cursor_info != last_cursor_info2)
2511 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2513 last_cursor_info2 = cursor_info;
2517 /* ========================================================================= */
2518 /* audio functions */
2519 /* ========================================================================= */
2521 void SDLOpenAudio(void)
2523 if (program.headless)
2526 #if !defined(TARGET_SDL2)
2527 if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
2528 SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
2531 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2533 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2537 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2538 AUDIO_NUM_CHANNELS_STEREO,
2539 setup.system.audio_fragment_size) < 0)
2541 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2545 audio.sound_available = TRUE;
2546 audio.music_available = TRUE;
2547 audio.loops_available = TRUE;
2548 audio.sound_enabled = TRUE;
2550 /* set number of available mixer channels */
2551 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2552 audio.music_channel = MUSIC_CHANNEL;
2553 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2555 Mixer_InitChannels();
2558 void SDLCloseAudio(void)
2561 Mix_HaltChannel(-1);
2564 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2568 /* ========================================================================= */
2569 /* event functions */
2570 /* ========================================================================= */
2572 void SDLWaitEvent(Event *event)
2574 SDL_WaitEvent(event);
2577 void SDLHandleWindowManagerEvent(Event *event)
2580 #if defined(PLATFORM_WIN32)
2581 // experimental drag and drop code
2583 SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
2584 SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
2586 #if defined(TARGET_SDL2)
2587 if (syswmmsg->msg.win.msg == WM_DROPFILES)
2589 if (syswmmsg->msg == WM_DROPFILES)
2592 #if defined(TARGET_SDL2)
2593 HDROP hdrop = (HDROP)syswmmsg->msg.win.wParam;
2595 HDROP hdrop = (HDROP)syswmmsg->wParam;
2599 printf("::: SDL_SYSWMEVENT:\n");
2601 num_files = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
2603 for (i = 0; i < num_files; i++)
2605 int buffer_len = DragQueryFile(hdrop, i, NULL, 0);
2606 char buffer[buffer_len + 1];
2608 DragQueryFile(hdrop, i, buffer, buffer_len + 1);
2610 printf("::: - '%s'\n", buffer);
2613 #if defined(TARGET_SDL2)
2614 DragFinish((HDROP)syswmmsg->msg.win.wParam);
2616 DragFinish((HDROP)syswmmsg->wParam);
2624 /* ========================================================================= */
2625 /* joystick functions */
2626 /* ========================================================================= */
2628 #if defined(TARGET_SDL2)
2629 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2631 static SDL_Joystick *sdl_joystick[MAX_PLAYERS]; // only joysticks supported
2633 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2634 static int sdl_js_axis[MAX_PLAYERS][2];
2635 static int sdl_js_button[MAX_PLAYERS][2];
2636 static boolean sdl_is_controller[MAX_PLAYERS];
2638 void SDLClearJoystickState(void)
2642 for (i = 0; i < MAX_PLAYERS; i++)
2644 for (j = 0; j < 2; j++)
2646 sdl_js_axis_raw[i][j] = -1;
2647 sdl_js_axis[i][j] = 0;
2648 sdl_js_button[i][j] = 0;
2653 boolean SDLOpenJoystick(int nr)
2655 if (nr < 0 || nr >= MAX_PLAYERS)
2658 #if defined(TARGET_SDL2)
2659 sdl_is_controller[nr] = SDL_IsGameController(nr);
2661 sdl_is_controller[nr] = FALSE;
2665 Error(ERR_DEBUG, "opening joystick %d (%s)",
2666 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2669 #if defined(TARGET_SDL2)
2670 if (sdl_is_controller[nr])
2671 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2673 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2675 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2678 return (sdl_joystick[nr] != NULL);
2681 void SDLCloseJoystick(int nr)
2683 if (nr < 0 || nr >= MAX_PLAYERS)
2687 Error(ERR_DEBUG, "closing joystick %d", nr);
2690 #if defined(TARGET_SDL2)
2691 if (sdl_is_controller[nr])
2692 SDL_GameControllerClose(sdl_joystick[nr]);
2694 SDL_JoystickClose(sdl_joystick[nr]);
2696 SDL_JoystickClose(sdl_joystick[nr]);
2699 sdl_joystick[nr] = NULL;
2702 boolean SDLCheckJoystickOpened(int nr)
2704 if (nr < 0 || nr >= MAX_PLAYERS)
2707 #if defined(TARGET_SDL2)
2708 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2710 return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
2714 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2716 #if defined(TARGET_SDL2)
2717 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2718 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2719 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2720 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2722 int axis_id = axis_id_raw % 2;
2725 if (nr < 0 || nr >= MAX_PLAYERS)
2731 // prevent (slightly jittering, but centered) axis A from resetting axis B
2732 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2733 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2736 sdl_js_axis[nr][axis_id] = axis_value;
2737 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2740 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2742 #if defined(TARGET_SDL2)
2743 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2744 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2745 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2746 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2747 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2748 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2749 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2750 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2753 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2754 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2755 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2756 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2757 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2758 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2759 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2760 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2762 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2763 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2764 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2765 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2766 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2768 int button_id = button_id_raw % 2;
2771 if (nr < 0 || nr >= MAX_PLAYERS)
2774 if (button_id == -1)
2777 sdl_js_button[nr][button_id] = button_state;
2780 void HandleJoystickEvent(Event *event)
2784 #if defined(TARGET_SDL2)
2785 case SDL_CONTROLLERDEVICEADDED:
2787 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2788 event->cdevice.which);
2793 case SDL_CONTROLLERDEVICEREMOVED:
2795 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2796 event->cdevice.which);
2801 case SDL_CONTROLLERAXISMOTION:
2803 Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2804 event->caxis.which, event->caxis.axis, event->caxis.value);
2806 setJoystickAxis(event->caxis.which,
2808 event->caxis.value);
2811 case SDL_CONTROLLERBUTTONDOWN:
2813 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2814 event->cbutton.which, event->cbutton.button);
2816 setJoystickButton(event->cbutton.which,
2817 event->cbutton.button,
2821 case SDL_CONTROLLERBUTTONUP:
2823 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2824 event->cbutton.which, event->cbutton.button);
2826 setJoystickButton(event->cbutton.which,
2827 event->cbutton.button,
2832 case SDL_JOYAXISMOTION:
2833 if (sdl_is_controller[event->jaxis.which])
2837 Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2838 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2840 if (event->jaxis.axis < 4)
2841 setJoystickAxis(event->jaxis.which,
2843 event->jaxis.value);
2846 case SDL_JOYBUTTONDOWN:
2847 if (sdl_is_controller[event->jaxis.which])
2851 Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2852 event->jbutton.which, event->jbutton.button);
2854 if (event->jbutton.button < 4)
2855 setJoystickButton(event->jbutton.which,
2856 event->jbutton.button,
2860 case SDL_JOYBUTTONUP:
2861 if (sdl_is_controller[event->jaxis.which])
2865 Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2866 event->jbutton.which, event->jbutton.button);
2868 if (event->jbutton.button < 4)
2869 setJoystickButton(event->jbutton.which,
2870 event->jbutton.button,
2879 void SDLInitJoysticks(void)
2881 static boolean sdl_joystick_subsystem_initialized = FALSE;
2882 boolean print_warning = !sdl_joystick_subsystem_initialized;
2883 #if defined(TARGET_SDL2)
2884 char *mappings_file_base = getPath2(options.conf_directory,
2885 GAMECONTROLLER_BASENAME);
2886 char *mappings_file_user = getPath2(getUserGameDataDir(),
2887 GAMECONTROLLER_BASENAME);
2892 if (!sdl_joystick_subsystem_initialized)
2894 sdl_joystick_subsystem_initialized = TRUE;
2896 #if defined(TARGET_SDL2)
2897 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2899 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2901 if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
2904 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2908 #if defined(TARGET_SDL2)
2909 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2911 /* the included game controller base mappings should always be found */
2912 if (num_mappings == -1)
2913 Error(ERR_WARN, "no game controller base mappings found");
2916 Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2919 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2922 /* the personal game controller user mappings may or may not be found */
2923 if (num_mappings == -1)
2924 Error(ERR_WARN, "no game controller user mappings found");
2926 Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2928 Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2931 checked_free(mappings_file_base);
2932 checked_free(mappings_file_user);
2935 for (i = 0; i < SDL_NumJoysticks(); i++)
2937 const char *name, *type;
2939 if (SDL_IsGameController(i))
2941 name = SDL_GameControllerNameForIndex(i);
2942 type = "game controller";
2946 name = SDL_JoystickNameForIndex(i);
2950 Error(ERR_INFO, "- joystick %d (%s): '%s'",
2951 i, type, (name ? name : "(Unknown)"));
2957 /* assign joysticks from configured to connected joystick for all players */
2958 for (i = 0; i < MAX_PLAYERS; i++)
2960 /* get configured joystick for this player */
2961 char *device_name = setup.input[i].joy.device_name;
2962 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2964 if (joystick_nr >= SDL_NumJoysticks())
2966 if (setup.input[i].use_joystick && print_warning)
2967 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2972 /* store configured joystick number for each player */
2973 joystick.nr[i] = joystick_nr;
2976 /* now open all connected joysticks (regardless if configured or not) */
2977 for (i = 0; i < SDL_NumJoysticks(); i++)
2979 /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
2980 if (SDLCheckJoystickOpened(i))
2981 SDLCloseJoystick(i);
2983 if (SDLOpenJoystick(i))
2984 joystick.status = JOYSTICK_ACTIVATED;
2985 else if (print_warning)
2986 Error(ERR_WARN, "cannot open joystick %d", i);
2989 SDLClearJoystickState();
2992 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2994 if (nr < 0 || nr >= MAX_PLAYERS)
2998 *x = sdl_js_axis[nr][0];
3000 *y = sdl_js_axis[nr][1];
3003 *b1 = sdl_js_button[nr][0];
3005 *b2 = sdl_js_button[nr][1];
3011 /* ========================================================================= */
3012 /* touch input overlay functions */
3013 /* ========================================================================= */
3015 #if defined(USE_TOUCH_INPUT_OVERLAY)
3016 static void DrawTouchInputOverlay_ShowGrid(int alpha)
3019 int grid_xsize = overlay.grid_xsize;
3020 int grid_ysize = overlay.grid_ysize;
3023 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
3024 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
3026 for (x = 0; x < grid_xsize; x++)
3028 rect.x = (x + 0) * video.screen_width / grid_xsize;
3029 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
3031 for (y = 0; y < grid_ysize; y++)
3033 rect.y = (y + 0) * video.screen_height / grid_ysize;
3034 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
3036 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
3037 SDL_RenderDrawRect(sdl_renderer, &rect);
3041 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3044 static void RenderFillRectangle(int x, int y, int width, int height)
3046 SDL_Rect rect = { x, y, width, height };
3048 SDL_RenderFillRect(sdl_renderer, &rect);
3051 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
3053 static int alpha_direction = 0;
3054 static int alpha_highlight = 0;
3055 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3056 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3058 int grid_xsize = overlay.grid_xsize;
3059 int grid_ysize = overlay.grid_ysize;
3062 if (alpha == alpha_max)
3064 if (alpha_direction < 0)
3066 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
3068 if (alpha_highlight == 0)
3069 alpha_direction = 1;
3073 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
3075 if (alpha_highlight == alpha_max)
3076 alpha_direction = -1;
3081 alpha_direction = 1;
3082 alpha_highlight = alpha;
3085 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
3087 for (x = 0; x < grid_xsize; x++)
3089 for (y = 0; y < grid_ysize; y++)
3091 int grid_button = overlay.grid_button[x][y];
3092 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
3093 int alpha_draw = alpha;
3094 int outline_border = MV_NONE;
3095 int border_size = 2;
3096 boolean draw_outlined = setup.touch.draw_outlined;
3097 boolean draw_pressed = setup.touch.draw_pressed;
3099 if (grid_button == CHAR_GRID_BUTTON_NONE)
3102 if (grid_button == overlay.grid_button_highlight)
3103 alpha_draw = alpha_highlight;
3105 if (draw_pressed && overlay.grid_button_action & grid_button_action)
3108 draw_outlined = FALSE;
3110 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
3113 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
3115 rect.x = (x + 0) * video.screen_width / grid_xsize;
3116 rect.y = (y + 0) * video.screen_height / grid_ysize;
3117 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
3118 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
3120 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
3122 rect.x += border_size;
3123 rect.w -= border_size;
3125 outline_border |= MV_LEFT;
3128 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
3130 rect.w -= border_size;
3132 outline_border |= MV_RIGHT;
3135 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
3137 rect.y += border_size;
3138 rect.h -= border_size;
3140 outline_border |= MV_UP;
3143 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
3145 rect.h -= border_size;
3147 outline_border |= MV_DOWN;
3152 int rect_x = rect.x +
3153 (outline_border & MV_LEFT ? border_size : 0);
3154 int rect_w = rect.w -
3155 (outline_border & MV_LEFT ? border_size : 0) -
3156 (outline_border & MV_RIGHT ? border_size : 0);
3158 if (outline_border & MV_LEFT)
3159 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
3161 if (outline_border & MV_RIGHT)
3162 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
3163 border_size, rect.h);
3165 if (outline_border & MV_UP)
3166 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
3168 if (outline_border & MV_DOWN)
3169 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
3170 rect_w, border_size);
3174 SDL_RenderFillRect(sdl_renderer, &rect);
3179 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3182 static void DrawTouchInputOverlay(void)
3184 static SDL_Texture *texture = NULL;
3185 static boolean initialized = FALSE;
3186 static boolean deactivated = TRUE;
3187 static boolean show_grid = FALSE;
3188 static int width = 0, height = 0;
3189 static int alpha_last = -1;
3190 static int alpha = 0;
3191 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3192 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3193 boolean active = (overlay.enabled && overlay.active);
3195 if (!active && deactivated)
3200 if (alpha < alpha_max)
3201 alpha = MIN(alpha + alpha_step, alpha_max);
3203 deactivated = FALSE;
3207 alpha = MAX(0, alpha - alpha_step);
3213 if (overlay.show_grid)
3215 else if (deactivated)
3219 DrawTouchInputOverlay_ShowGrid(alpha);
3221 DrawTouchInputOverlay_ShowGridButtons(alpha);
3226 // !!! VIRTUAL BUTTONS FROM IMAGE FILE NOT USED ANYMORE !!!
3230 char *basename = "overlay/VirtualButtons.png";
3231 char *filename = getCustomImageFilename(basename);
3233 if (filename == NULL)
3234 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
3236 SDL_Surface *surface;
3238 if ((surface = IMG_Load(filename)) == NULL)
3239 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
3242 height = surface->h;
3244 /* set black pixel to transparent if no alpha channel / transparent color */
3245 if (!SDLHasAlpha(surface) &&
3246 !SDLHasColorKey(surface))
3247 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
3248 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
3250 if ((texture = SDLCreateTextureFromSurface(surface)) == NULL)
3251 Error(ERR_EXIT, "SDLCreateTextureFromSurface() failed");
3253 SDL_FreeSurface(surface);
3255 SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
3260 if (alpha != alpha_last)
3261 SDL_SetTextureAlphaMod(texture, alpha);
3265 float ratio_overlay = (float) width / height;
3266 float ratio_screen = (float) video.screen_width / video.screen_height;
3267 int width_scaled, height_scaled;
3270 if (ratio_overlay > ratio_screen)
3272 width_scaled = video.screen_width;
3273 height_scaled = video.screen_height * ratio_screen / ratio_overlay;
3275 ypos = video.screen_height - height_scaled;
3279 width_scaled = video.screen_width * ratio_overlay / ratio_screen;
3280 height_scaled = video.screen_height;
3281 xpos = (video.screen_width - width_scaled) / 2;
3285 SDL_Rect src_rect = { 0, 0, width, height };
3286 SDL_Rect dst_rect = { xpos, ypos, width_scaled, height_scaled };
3288 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);