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 static void SDLSetWindowIcon(char *basename)
250 /* (setting the window icon on Mac OS X would replace the high-quality
251 dock icon with the currently smaller (and uglier) icon from file) */
253 #if !defined(PLATFORM_MACOSX)
254 char *filename = getCustomImageFilename(basename);
255 SDL_Surface *surface;
257 if (filename == NULL)
259 Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
264 if ((surface = IMG_Load(filename)) == NULL)
266 Error(ERR_WARN, "IMG_Load() failed: %s", SDL_GetError());
271 /* set transparent color */
272 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
273 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
275 #if defined(TARGET_SDL2)
276 SDL_SetWindowIcon(sdl_window, surface);
278 SDL_WM_SetIcon(surface, NULL);
283 #if defined(TARGET_SDL2)
285 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
286 SDL_PixelFormat *format2)
288 return (format1->format == format2->format &&
289 format1->BitsPerPixel == format2->BitsPerPixel &&
290 format1->BytesPerPixel == format2->BytesPerPixel &&
291 format1->Rmask == format2->Rmask &&
292 format1->Gmask == format2->Gmask &&
293 format1->Bmask == format2->Bmask);
296 static Pixel SDLGetColorKey(SDL_Surface *surface)
300 if (SDL_GetColorKey(surface, &color_key) != 0)
306 static boolean SDLHasColorKey(SDL_Surface *surface)
308 return (SDLGetColorKey(surface) != -1);
311 static boolean SDLHasAlpha(SDL_Surface *surface)
313 SDL_BlendMode blend_mode;
315 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
318 return (blend_mode == SDL_BLENDMODE_BLEND);
321 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
323 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
325 SDL_SetSurfaceBlendMode(surface, blend_mode);
326 SDL_SetSurfaceAlphaMod(surface, alpha);
329 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
331 SDL_PixelFormat format;
332 SDL_Surface *new_surface;
337 if (backbuffer && backbuffer->surface)
339 format = *backbuffer->surface->format;
340 format.Amask = surface->format->Amask; // keep alpha channel
344 format = *surface->format;
347 new_surface = SDL_ConvertSurface(surface, &format, 0);
349 if (new_surface == NULL)
350 Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
355 boolean SDLSetNativeSurface(SDL_Surface **surface)
357 SDL_Surface *new_surface;
359 if (surface == NULL ||
361 backbuffer == NULL ||
362 backbuffer->surface == NULL)
365 // if pixel format already optimized for destination surface, do nothing
366 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
369 new_surface = SDLGetNativeSurface(*surface);
371 SDL_FreeSurface(*surface);
373 *surface = new_surface;
380 static Pixel SDLGetColorKey(SDL_Surface *surface)
382 if ((surface->flags & SDL_SRCCOLORKEY) == 0)
385 return surface->format->colorkey;
388 static boolean SDLHasColorKey(SDL_Surface *surface)
390 return (SDLGetColorKey(surface) != -1);
393 static boolean SDLHasAlpha(SDL_Surface *surface)
395 return ((surface->flags & SDL_SRCALPHA) != 0);
398 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
400 SDL_SetAlpha(surface, (set ? SDL_SRCALPHA : 0), alpha);
403 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
405 SDL_Surface *new_surface;
410 if (!video.initialized)
411 new_surface = SDL_ConvertSurface(surface, surface->format, SURFACE_FLAGS);
412 else if (SDLHasAlpha(surface))
413 new_surface = SDL_DisplayFormatAlpha(surface);
415 new_surface = SDL_DisplayFormat(surface);
417 if (new_surface == NULL)
418 Error(ERR_EXIT, "%s() failed: %s",
419 (video.initialized ? "SDL_DisplayFormat" : "SDL_ConvertSurface"),
425 boolean SDLSetNativeSurface(SDL_Surface **surface)
427 SDL_Surface *new_surface;
429 if (surface == NULL ||
434 new_surface = SDLGetNativeSurface(*surface);
436 SDL_FreeSurface(*surface);
438 *surface = new_surface;
445 #if defined(TARGET_SDL2)
446 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
448 if (program.headless)
451 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
454 Error(ERR_EXIT, "SDL_CreateTextureFromSurface() failed: %s",
461 void SDLCreateBitmapTextures(Bitmap *bitmap)
463 #if defined(TARGET_SDL2)
468 SDL_DestroyTexture(bitmap->texture);
469 if (bitmap->texture_masked)
470 SDL_DestroyTexture(bitmap->texture_masked);
472 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
473 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
477 void SDLFreeBitmapTextures(Bitmap *bitmap)
479 #if defined(TARGET_SDL2)
484 SDL_DestroyTexture(bitmap->texture);
485 if (bitmap->texture_masked)
486 SDL_DestroyTexture(bitmap->texture_masked);
488 bitmap->texture = NULL;
489 bitmap->texture_masked = NULL;
493 void SDLInitVideoDisplay(void)
495 #if !defined(TARGET_SDL2)
496 if (!strEqual(setup.system.sdl_videodriver, ARG_DEFAULT))
497 SDL_putenv(getStringCat2("SDL_VIDEODRIVER=", setup.system.sdl_videodriver));
499 SDL_putenv("SDL_VIDEO_CENTERED=1");
502 /* initialize SDL video */
503 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
504 Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
506 /* set default SDL depth */
507 #if !defined(TARGET_SDL2)
508 video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
510 video.default_depth = 32; // (how to determine video depth in SDL2?)
514 inline static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
516 if (program.headless)
519 video.window_scaling_percent = setup.window_scaling_percent;
520 video.window_scaling_quality = setup.window_scaling_quality;
522 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
524 #if defined(TARGET_SDL2)
525 // SDL 2.0: support for (desktop) fullscreen mode available
526 video.fullscreen_available = TRUE;
528 // SDL 1.2: no support for fullscreen mode in R'n'D anymore
529 video.fullscreen_available = FALSE;
532 /* open SDL video output device (window or fullscreen mode) */
533 if (!SDLSetVideoMode(fullscreen))
534 Error(ERR_EXIT, "setting video mode failed");
536 /* !!! SDL2 can only set the window icon if the window already exists !!! */
537 /* set window icon */
538 SDLSetWindowIcon(program.icon_filename);
540 /* set window and icon title */
544 inline static void SDLInitVideoBuffer_DrawBuffer()
546 /* SDL cannot directly draw to the visible video framebuffer like X11,
547 but always uses a backbuffer, which is then blitted to the visible
548 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
549 visible video framebuffer with 'SDL_Flip', if the hardware supports
550 this). Therefore do not use an additional backbuffer for drawing, but
551 use a symbolic buffer (distinguishable from the SDL backbuffer) called
552 'window', which indicates that the SDL backbuffer should be updated to
553 the visible video framebuffer when attempting to blit to it.
555 For convenience, it seems to be a good idea to create this symbolic
556 buffer 'window' at the same size as the SDL backbuffer. Although it
557 should never be drawn to directly, it would do no harm nevertheless. */
559 /* create additional (symbolic) buffer for double-buffering */
560 ReCreateBitmap(&window, video.width, video.height);
562 /* create dummy drawing buffer for headless mode, if needed */
563 if (program.headless)
564 ReCreateBitmap(&backbuffer, video.width, video.height);
567 void SDLInitVideoBuffer(boolean fullscreen)
569 SDLInitVideoBuffer_VideoBuffer(fullscreen);
570 SDLInitVideoBuffer_DrawBuffer();
573 static boolean SDLCreateScreen(boolean fullscreen)
575 SDL_Surface *new_surface = NULL;
577 #if defined(TARGET_SDL2)
578 int surface_flags_window = SURFACE_FLAGS | SDL_WINDOW_RESIZABLE;
579 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
581 int surface_flags_window = SURFACE_FLAGS;
582 int surface_flags_fullscreen = SURFACE_FLAGS; // (no fullscreen in SDL 1.2)
585 #if defined(TARGET_SDL2)
587 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
589 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
590 _without_ enabling 2D/3D acceleration and/or guest additions installed,
591 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
592 it will try to use accelerated graphics and apparently fails miserably) */
593 int renderer_flags = SDL_RENDERER_SOFTWARE;
596 SDLSetScreenSizeAndOffsets(video.width, video.height);
599 int width = video.width;
600 int height = video.height;
601 int screen_width = video.screen_width;
602 int screen_height = video.screen_height;
603 int surface_flags = (fullscreen ? surface_flags_fullscreen :
604 surface_flags_window);
606 // default window size is unscaled
607 video.window_width = screen_width;
608 video.window_height = screen_height;
610 #if defined(TARGET_SDL2)
612 // store if initial screen mode is fullscreen mode when changing screen size
613 video.fullscreen_initial = fullscreen;
615 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
617 video.window_width = window_scaling_factor * screen_width;
618 video.window_height = window_scaling_factor * screen_height;
620 if (sdl_texture_stream)
622 SDL_DestroyTexture(sdl_texture_stream);
623 sdl_texture_stream = NULL;
626 if (sdl_texture_target)
628 SDL_DestroyTexture(sdl_texture_target);
629 sdl_texture_target = NULL;
632 if (!(fullscreen && fullscreen_enabled))
636 SDL_DestroyRenderer(sdl_renderer);
642 SDL_DestroyWindow(sdl_window);
647 if (sdl_window == NULL)
648 sdl_window = SDL_CreateWindow(program.window_title,
649 SDL_WINDOWPOS_CENTERED,
650 SDL_WINDOWPOS_CENTERED,
655 if (sdl_window != NULL)
657 if (sdl_renderer == NULL)
658 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
660 if (sdl_renderer != NULL)
662 SDL_RenderSetLogicalSize(sdl_renderer, screen_width, screen_height);
663 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
664 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
666 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
667 SDL_PIXELFORMAT_ARGB8888,
668 SDL_TEXTUREACCESS_STREAMING,
671 if (SDL_RenderTargetSupported(sdl_renderer))
672 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
673 SDL_PIXELFORMAT_ARGB8888,
674 SDL_TEXTUREACCESS_TARGET,
677 if (sdl_texture_stream != NULL)
679 // use SDL default values for RGB masks and no alpha channel
680 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
682 if (new_surface == NULL)
683 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
687 Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
692 Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
697 Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
702 if (gfx.final_screen_bitmap == NULL)
703 gfx.final_screen_bitmap = CreateBitmapStruct();
705 gfx.final_screen_bitmap->width = width;
706 gfx.final_screen_bitmap->height = height;
708 gfx.final_screen_bitmap->surface =
709 SDL_SetVideoMode(width, height, video.depth, surface_flags);
711 if (gfx.final_screen_bitmap->surface != NULL)
714 SDL_CreateRGBSurface(surface_flags, width, height, video.depth, 0,0,0, 0);
716 if (new_surface == NULL)
717 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
720 new_surface = gfx.final_screen_bitmap->surface;
721 gfx.final_screen_bitmap = NULL;
727 Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
731 #if defined(TARGET_SDL2)
732 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
733 if (new_surface != NULL)
734 fullscreen_enabled = fullscreen;
737 if (backbuffer == NULL)
738 backbuffer = CreateBitmapStruct();
740 backbuffer->width = video.width;
741 backbuffer->height = video.height;
743 if (backbuffer->surface)
744 SDL_FreeSurface(backbuffer->surface);
746 backbuffer->surface = new_surface;
748 return (new_surface != NULL);
751 boolean SDLSetVideoMode(boolean fullscreen)
753 boolean success = FALSE;
757 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
759 /* switch display to fullscreen mode, if available */
760 success = SDLCreateScreen(TRUE);
764 /* switching display to fullscreen mode failed -- do not try it again */
765 video.fullscreen_available = FALSE;
769 video.fullscreen_enabled = TRUE;
773 if ((!fullscreen && video.fullscreen_enabled) || !success)
775 /* switch display to window mode */
776 success = SDLCreateScreen(FALSE);
780 /* switching display to window mode failed -- should not happen */
784 video.fullscreen_enabled = FALSE;
785 video.window_scaling_percent = setup.window_scaling_percent;
786 video.window_scaling_quality = setup.window_scaling_quality;
788 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
792 #if defined(TARGET_SDL2)
793 SDLRedrawWindow(); // map window
797 #if defined(PLATFORM_WIN32)
798 // experimental drag and drop code
800 SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
803 SDL_SysWMinfo wminfo;
805 boolean wminfo_success = FALSE;
807 SDL_VERSION(&wminfo.version);
808 #if defined(TARGET_SDL2)
810 wminfo_success = SDL_GetWindowWMInfo(sdl_window, &wminfo);
812 wminfo_success = (SDL_GetWMInfo(&wminfo) == 1);
817 #if defined(TARGET_SDL2)
818 hwnd = wminfo.info.win.window;
820 hwnd = wminfo.window;
823 DragAcceptFiles(hwnd, TRUE);
832 void SDLSetWindowTitle()
834 #if defined(TARGET_SDL2)
835 if (sdl_window == NULL)
838 SDL_SetWindowTitle(sdl_window, program.window_title);
840 SDL_WM_SetCaption(program.window_title, program.window_title);
844 #if defined(TARGET_SDL2)
845 void SDLSetWindowScaling(int window_scaling_percent)
847 if (sdl_window == NULL)
850 float window_scaling_factor = (float)window_scaling_percent / 100;
851 int new_window_width = (int)(window_scaling_factor * video.screen_width);
852 int new_window_height = (int)(window_scaling_factor * video.screen_height);
854 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
856 video.window_scaling_percent = window_scaling_percent;
857 video.window_width = new_window_width;
858 video.window_height = new_window_height;
863 void SDLSetWindowScalingQuality(char *window_scaling_quality)
865 SDL_Texture *new_texture;
867 if (sdl_texture_stream == NULL)
870 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
872 new_texture = SDL_CreateTexture(sdl_renderer,
873 SDL_PIXELFORMAT_ARGB8888,
874 SDL_TEXTUREACCESS_STREAMING,
875 video.width, video.height);
877 if (new_texture != NULL)
879 SDL_DestroyTexture(sdl_texture_stream);
881 sdl_texture_stream = new_texture;
884 if (SDL_RenderTargetSupported(sdl_renderer))
885 new_texture = SDL_CreateTexture(sdl_renderer,
886 SDL_PIXELFORMAT_ARGB8888,
887 SDL_TEXTUREACCESS_TARGET,
888 video.width, video.height);
892 if (new_texture != NULL)
894 SDL_DestroyTexture(sdl_texture_target);
896 sdl_texture_target = new_texture;
901 video.window_scaling_quality = window_scaling_quality;
904 void SDLSetWindowFullscreen(boolean fullscreen)
906 if (sdl_window == NULL)
909 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
911 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
912 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
914 // if screen size was changed in fullscreen mode, correct desktop window size
915 if (!fullscreen && video.fullscreen_initial)
917 SDLSetWindowScaling(setup.window_scaling_percent);
918 SDL_SetWindowPosition(sdl_window,
919 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
921 video.fullscreen_initial = FALSE;
925 void SDLSetDisplaySize()
927 SDL_Rect display_bounds;
929 SDL_GetDisplayBounds(0, &display_bounds);
931 video.display_width = display_bounds.w;
932 video.display_height = display_bounds.h;
935 Error(ERR_DEBUG, "SDL real screen size: %d x %d",
936 video.display_width, video.display_height);
940 void SDLSetScreenSizeAndOffsets(int width, int height)
942 // set default video screen size and offsets
943 video.screen_width = width;
944 video.screen_height = height;
945 video.screen_xoffset = 0;
946 video.screen_yoffset = 0;
948 #if defined(USE_COMPLETE_DISPLAY)
949 float ratio_video = (float) width / height;
950 float ratio_display = (float) video.display_width / video.display_height;
952 if (ratio_video != ratio_display)
954 // adjust drawable screen size to cover the whole device display
956 if (ratio_video < ratio_display)
957 video.screen_width *= ratio_display / ratio_video;
959 video.screen_height *= ratio_video / ratio_display;
961 video.screen_xoffset = (video.screen_width - width) / 2;
962 video.screen_yoffset = (video.screen_height - height) / 2;
965 Error(ERR_DEBUG, "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
967 video.screen_width, video.screen_height,
968 ratio_video, ratio_display);
974 void SDLSetScreenSizeForRenderer(int width, int height)
976 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
979 void SDLSetScreenProperties()
981 SDLSetScreenSizeAndOffsets(video.width, video.height);
982 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
987 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
989 #if defined(TARGET_SDL2)
990 video.screen_rendering_mode =
991 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
992 SPECIAL_RENDERING_BITMAP :
993 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
994 SPECIAL_RENDERING_TARGET:
995 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
996 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
998 video.screen_rendering_mode = SPECIAL_RENDERING_BITMAP;
1002 void SDLRedrawWindow()
1004 UpdateScreen_WithoutFrameDelay(NULL);
1007 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
1010 if (program.headless)
1013 SDL_Surface *surface =
1014 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
1016 if (surface == NULL)
1017 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
1019 SDLSetNativeSurface(&surface);
1021 bitmap->surface = surface;
1024 void SDLFreeBitmapPointers(Bitmap *bitmap)
1026 if (bitmap->surface)
1027 SDL_FreeSurface(bitmap->surface);
1028 if (bitmap->surface_masked)
1029 SDL_FreeSurface(bitmap->surface_masked);
1031 bitmap->surface = NULL;
1032 bitmap->surface_masked = NULL;
1034 #if defined(TARGET_SDL2)
1035 if (bitmap->texture)
1036 SDL_DestroyTexture(bitmap->texture);
1037 if (bitmap->texture_masked)
1038 SDL_DestroyTexture(bitmap->texture_masked);
1040 bitmap->texture = NULL;
1041 bitmap->texture_masked = NULL;
1045 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1046 int src_x, int src_y, int width, int height,
1047 int dst_x, int dst_y, int mask_mode)
1049 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1050 SDL_Rect src_rect, dst_rect;
1055 src_rect.h = height;
1060 dst_rect.h = height;
1062 // if (src_bitmap != backbuffer || dst_bitmap != window)
1063 if (!(src_bitmap == backbuffer && dst_bitmap == window))
1064 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
1065 src_bitmap->surface_masked : src_bitmap->surface),
1066 &src_rect, real_dst_bitmap->surface, &dst_rect);
1068 if (dst_bitmap == window)
1069 UpdateScreen_WithFrameDelay(&dst_rect);
1072 void SDLBlitTexture(Bitmap *bitmap,
1073 int src_x, int src_y, int width, int height,
1074 int dst_x, int dst_y, int mask_mode)
1076 #if defined(TARGET_SDL2)
1077 SDL_Texture *texture;
1082 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1084 if (texture == NULL)
1090 src_rect.h = height;
1095 dst_rect.h = height;
1097 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1101 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1104 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1112 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1114 if (dst_bitmap == window)
1115 UpdateScreen_WithFrameDelay(&rect);
1118 void PrepareFadeBitmap(int draw_target)
1120 Bitmap *fade_bitmap =
1121 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1122 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1124 if (fade_bitmap == NULL)
1127 // copy backbuffer to fading buffer
1128 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1130 // add border and animations to fading buffer
1131 FinalizeScreen(draw_target);
1134 void SDLFadeRectangle(int x, int y, int width, int height,
1135 int fade_mode, int fade_delay, int post_delay,
1136 void (*draw_border_function)(void))
1138 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1139 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1140 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1141 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1142 SDL_Surface *surface_screen = backbuffer->surface;
1143 SDL_Rect src_rect, dst_rect;
1145 int src_x = x, src_y = y;
1146 int dst_x = x, dst_y = y;
1147 unsigned int time_last, time_current;
1149 // store function for drawing global masked border
1150 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1152 // deactivate drawing of global border while fading, if needed
1153 if (draw_border_function == NULL)
1154 gfx.draw_global_border_function = NULL;
1159 src_rect.h = height;
1163 dst_rect.w = width; /* (ignored) */
1164 dst_rect.h = height; /* (ignored) */
1166 dst_rect2 = dst_rect;
1168 // before fading in, store backbuffer (without animation graphics)
1169 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1170 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1172 /* copy source and target surfaces to temporary surfaces for fading */
1173 if (fade_mode & FADE_TYPE_TRANSFORM)
1175 // (source and target fading buffer already prepared)
1177 else if (fade_mode & FADE_TYPE_FADE_IN)
1179 // (target fading buffer already prepared)
1180 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1182 else /* FADE_TYPE_FADE_OUT */
1184 // (source fading buffer already prepared)
1185 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1188 time_current = SDL_GetTicks();
1190 if (fade_mode == FADE_MODE_MELT)
1192 boolean done = FALSE;
1193 int melt_pixels = 2;
1194 int melt_columns = width / melt_pixels;
1195 int ypos[melt_columns];
1196 int max_steps = height / 8 + 32;
1201 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1203 SDLSetAlpha(surface_target, FALSE, 0); /* disable alpha blending */
1205 ypos[0] = -GetSimpleRandom(16);
1207 for (i = 1 ; i < melt_columns; i++)
1209 int r = GetSimpleRandom(3) - 1; /* randomly choose from { -1, 0, -1 } */
1211 ypos[i] = ypos[i - 1] + r;
1224 time_last = time_current;
1225 time_current = SDL_GetTicks();
1226 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1227 steps_final = MIN(MAX(0, steps), max_steps);
1231 done = (steps_done >= steps_final);
1233 for (i = 0 ; i < melt_columns; i++)
1241 else if (ypos[i] < height)
1246 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1248 if (ypos[i] + dy >= height)
1249 dy = height - ypos[i];
1251 /* copy part of (appearing) target surface to upper area */
1252 src_rect.x = src_x + i * melt_pixels;
1253 // src_rect.y = src_y + ypos[i];
1255 src_rect.w = melt_pixels;
1257 src_rect.h = ypos[i] + dy;
1259 dst_rect.x = dst_x + i * melt_pixels;
1260 // dst_rect.y = dst_y + ypos[i];
1263 if (steps_done >= steps_final)
1264 SDL_BlitSurface(surface_target, &src_rect,
1265 surface_screen, &dst_rect);
1269 /* copy part of (disappearing) source surface to lower area */
1270 src_rect.x = src_x + i * melt_pixels;
1272 src_rect.w = melt_pixels;
1273 src_rect.h = height - ypos[i];
1275 dst_rect.x = dst_x + i * melt_pixels;
1276 dst_rect.y = dst_y + ypos[i];
1278 if (steps_done >= steps_final)
1279 SDL_BlitSurface(surface_source, &src_rect,
1280 surface_screen, &dst_rect);
1286 src_rect.x = src_x + i * melt_pixels;
1288 src_rect.w = melt_pixels;
1289 src_rect.h = height;
1291 dst_rect.x = dst_x + i * melt_pixels;
1294 if (steps_done >= steps_final)
1295 SDL_BlitSurface(surface_target, &src_rect,
1296 surface_screen, &dst_rect);
1300 if (steps_done >= steps_final)
1302 if (draw_border_function != NULL)
1303 draw_border_function();
1305 UpdateScreen_WithFrameDelay(&dst_rect2);
1309 else if (fade_mode == FADE_MODE_CURTAIN)
1313 int xx_size = width / 2;
1315 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1317 SDLSetAlpha(surface_source, FALSE, 0); /* disable alpha blending */
1319 for (xx = 0; xx < xx_size;)
1321 time_last = time_current;
1322 time_current = SDL_GetTicks();
1323 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1324 xx_final = MIN(MAX(0, xx), xx_size);
1329 src_rect.h = height;
1334 /* draw new (target) image to screen buffer */
1335 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1337 if (xx_final < xx_size)
1339 src_rect.w = xx_size - xx_final;
1340 src_rect.h = height;
1342 /* draw old (source) image to screen buffer (left side) */
1344 src_rect.x = src_x + xx_final;
1347 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1349 /* draw old (source) image to screen buffer (right side) */
1351 src_rect.x = src_x + xx_size;
1352 dst_rect.x = dst_x + xx_size + xx_final;
1354 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1357 if (draw_border_function != NULL)
1358 draw_border_function();
1360 /* only update the region of the screen that is affected from fading */
1361 UpdateScreen_WithFrameDelay(&dst_rect2);
1364 else /* fading in, fading out or cross-fading */
1369 for (alpha = 0.0; alpha < 255.0;)
1371 time_last = time_current;
1372 time_current = SDL_GetTicks();
1373 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1374 alpha_final = MIN(MAX(0, alpha), 255);
1376 /* draw existing (source) image to screen buffer */
1377 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1379 /* draw new (target) image to screen buffer using alpha blending */
1380 SDLSetAlpha(surface_target, TRUE, alpha_final);
1381 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1383 if (draw_border_function != NULL)
1384 draw_border_function();
1386 /* only update the region of the screen that is affected from fading */
1387 UpdateScreen_WithFrameDelay(&dst_rect);
1393 unsigned int time_post_delay;
1395 time_current = SDL_GetTicks();
1396 time_post_delay = time_current + post_delay;
1398 while (time_current < time_post_delay)
1400 // updating the screen contains waiting for frame delay (non-busy)
1401 UpdateScreen_WithFrameDelay(NULL);
1403 time_current = SDL_GetTicks();
1407 // restore function for drawing global masked border
1408 gfx.draw_global_border_function = draw_global_border_function;
1410 // after fading in, restore backbuffer (without animation graphics)
1411 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1412 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1415 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1416 int to_x, int to_y, Uint32 color)
1418 SDL_Surface *surface = dst_bitmap->surface;
1422 swap_numbers(&from_x, &to_x);
1425 swap_numbers(&from_y, &to_y);
1429 rect.w = (to_x - from_x + 1);
1430 rect.h = (to_y - from_y + 1);
1432 SDL_FillRect(surface, &rect, color);
1435 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1436 int to_x, int to_y, Uint32 color)
1438 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1441 #if ENABLE_UNUSED_CODE
1442 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1443 int num_points, Uint32 color)
1448 for (i = 0; i < num_points - 1; i++)
1450 for (x = 0; x < line_width; x++)
1452 for (y = 0; y < line_width; y++)
1454 int dx = x - line_width / 2;
1455 int dy = y - line_width / 2;
1457 if ((x == 0 && y == 0) ||
1458 (x == 0 && y == line_width - 1) ||
1459 (x == line_width - 1 && y == 0) ||
1460 (x == line_width - 1 && y == line_width - 1))
1463 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1464 points[i+1].x + dx, points[i+1].y + dy, color);
1471 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1473 SDL_Surface *surface = src_bitmap->surface;
1475 switch (surface->format->BytesPerPixel)
1477 case 1: /* assuming 8-bpp */
1479 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1483 case 2: /* probably 15-bpp or 16-bpp */
1485 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1489 case 3: /* slow 24-bpp mode; usually not used */
1491 /* does this work? */
1492 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1496 shift = surface->format->Rshift;
1497 color |= *(pix + shift / 8) >> shift;
1498 shift = surface->format->Gshift;
1499 color |= *(pix + shift / 8) >> shift;
1500 shift = surface->format->Bshift;
1501 color |= *(pix + shift / 8) >> shift;
1507 case 4: /* probably 32-bpp */
1509 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1518 /* ========================================================================= */
1519 /* The following functions were taken from the SGE library */
1520 /* (SDL Graphics Extension Library) by Anders Lindström */
1521 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html */
1522 /* ========================================================================= */
1524 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1526 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1528 switch (surface->format->BytesPerPixel)
1532 /* Assuming 8-bpp */
1533 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1539 /* Probably 15-bpp or 16-bpp */
1540 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1546 /* Slow 24-bpp mode, usually not used */
1550 /* Gack - slow, but endian correct */
1551 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1552 shift = surface->format->Rshift;
1553 *(pix+shift/8) = color>>shift;
1554 shift = surface->format->Gshift;
1555 *(pix+shift/8) = color>>shift;
1556 shift = surface->format->Bshift;
1557 *(pix+shift/8) = color>>shift;
1563 /* Probably 32-bpp */
1564 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1571 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1572 Uint8 R, Uint8 G, Uint8 B)
1574 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1577 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1579 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1582 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1584 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1587 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1592 /* Gack - slow, but endian correct */
1593 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1594 shift = surface->format->Rshift;
1595 *(pix+shift/8) = color>>shift;
1596 shift = surface->format->Gshift;
1597 *(pix+shift/8) = color>>shift;
1598 shift = surface->format->Bshift;
1599 *(pix+shift/8) = color>>shift;
1602 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1604 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1607 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1609 switch (dest->format->BytesPerPixel)
1612 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1616 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1620 _PutPixel24(dest,x,y,color);
1624 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1629 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1631 if (SDL_MUSTLOCK(surface))
1633 if (SDL_LockSurface(surface) < 0)
1639 _PutPixel(surface, x, y, color);
1641 if (SDL_MUSTLOCK(surface))
1643 SDL_UnlockSurface(surface);
1647 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1648 Uint8 r, Uint8 g, Uint8 b)
1650 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1653 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1655 if (y >= 0 && y <= dest->h - 1)
1657 switch (dest->format->BytesPerPixel)
1660 return y*dest->pitch;
1664 return y*dest->pitch/2;
1668 return y*dest->pitch;
1672 return y*dest->pitch/4;
1680 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
1682 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1684 switch (surface->format->BytesPerPixel)
1688 /* Assuming 8-bpp */
1689 *((Uint8 *)surface->pixels + ypitch + x) = color;
1695 /* Probably 15-bpp or 16-bpp */
1696 *((Uint16 *)surface->pixels + ypitch + x) = color;
1702 /* Slow 24-bpp mode, usually not used */
1706 /* Gack - slow, but endian correct */
1707 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1708 shift = surface->format->Rshift;
1709 *(pix+shift/8) = color>>shift;
1710 shift = surface->format->Gshift;
1711 *(pix+shift/8) = color>>shift;
1712 shift = surface->format->Bshift;
1713 *(pix+shift/8) = color>>shift;
1719 /* Probably 32-bpp */
1720 *((Uint32 *)surface->pixels + ypitch + x) = color;
1727 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1732 if (SDL_MUSTLOCK(Surface))
1734 if (SDL_LockSurface(Surface) < 0)
1747 /* Do the clipping */
1748 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1752 if (x2 > Surface->w - 1)
1753 x2 = Surface->w - 1;
1760 SDL_FillRect(Surface, &l, Color);
1762 if (SDL_MUSTLOCK(Surface))
1764 SDL_UnlockSurface(Surface);
1768 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1769 Uint8 R, Uint8 G, Uint8 B)
1771 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1774 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
1785 /* Do the clipping */
1786 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1790 if (x2 > Surface->w - 1)
1791 x2 = Surface->w - 1;
1798 SDL_FillRect(Surface, &l, Color);
1801 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1806 if (SDL_MUSTLOCK(Surface))
1808 if (SDL_LockSurface(Surface) < 0)
1821 /* Do the clipping */
1822 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1826 if (y2 > Surface->h - 1)
1827 y2 = Surface->h - 1;
1834 SDL_FillRect(Surface, &l, Color);
1836 if (SDL_MUSTLOCK(Surface))
1838 SDL_UnlockSurface(Surface);
1842 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1843 Uint8 R, Uint8 G, Uint8 B)
1845 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1848 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
1859 /* Do the clipping */
1860 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1864 if (y2 > Surface->h - 1)
1865 y2 = Surface->h - 1;
1872 SDL_FillRect(Surface, &l, Color);
1875 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1876 Sint16 x2, Sint16 y2, Uint32 Color,
1877 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1880 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1885 sdx = (dx < 0) ? -1 : 1;
1886 sdy = (dy < 0) ? -1 : 1;
1898 for (x = 0; x < dx; x++)
1900 Callback(Surface, px, py, Color);
1914 for (y = 0; y < dy; y++)
1916 Callback(Surface, px, py, Color);
1930 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1931 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1932 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1935 sge_DoLine(Surface, X1, Y1, X2, Y2,
1936 SDL_MapRGB(Surface->format, R, G, B), Callback);
1939 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1942 if (SDL_MUSTLOCK(Surface))
1944 if (SDL_LockSurface(Surface) < 0)
1949 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1951 /* unlock the display */
1952 if (SDL_MUSTLOCK(Surface))
1954 SDL_UnlockSurface(Surface);
1958 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1959 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1961 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1964 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1966 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1971 -----------------------------------------------------------------------------
1972 quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1973 -----------------------------------------------------------------------------
1976 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1977 int width, int height, Uint32 color)
1981 for (y = src_y; y < src_y + height; y++)
1983 for (x = src_x; x < src_x + width; x++)
1985 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1987 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1992 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1993 int src_x, int src_y, int width, int height,
1994 int dst_x, int dst_y)
1998 for (y = 0; y < height; y++)
2000 for (x = 0; x < width; x++)
2002 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
2004 if (pixel != BLACK_PIXEL)
2005 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
2011 /* ========================================================================= */
2012 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
2013 /* (Rotozoomer) by Andreas Schiffler */
2014 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html */
2015 /* ========================================================================= */
2018 -----------------------------------------------------------------------------
2021 zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
2022 -----------------------------------------------------------------------------
2033 int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
2036 tColorRGBA *sp, *csp, *dp;
2040 sp = csp = (tColorRGBA *) src->pixels;
2041 dp = (tColorRGBA *) dst->pixels;
2042 dgap = dst->pitch - dst->w * 4;
2044 for (y = 0; y < dst->h; y++)
2048 for (x = 0; x < dst->w; x++)
2050 tColorRGBA *sp0 = sp;
2051 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
2052 tColorRGBA *sp00 = &sp0[0];
2053 tColorRGBA *sp01 = &sp0[1];
2054 tColorRGBA *sp10 = &sp1[0];
2055 tColorRGBA *sp11 = &sp1[1];
2058 /* create new color pixel from all four source color pixels */
2059 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
2060 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
2061 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
2062 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
2067 /* advance source pointers */
2070 /* advance destination pointer */
2074 /* advance source pointer */
2075 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2077 /* advance destination pointers */
2078 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2084 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2086 int x, y, *sax, *say, *csax, *csay;
2088 tColorRGBA *sp, *csp, *csp0, *dp;
2091 /* use specialized zoom function when scaling down to exactly half size */
2092 if (src->w == 2 * dst->w &&
2093 src->h == 2 * dst->h)
2094 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2096 /* variable setup */
2097 sx = (float) src->w / (float) dst->w;
2098 sy = (float) src->h / (float) dst->h;
2100 /* allocate memory for row increments */
2101 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2102 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2104 /* precalculate row increments */
2105 for (x = 0; x <= dst->w; x++)
2106 *csax++ = (int)(sx * x);
2108 for (y = 0; y <= dst->h; y++)
2109 *csay++ = (int)(sy * y);
2112 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2113 dp = (tColorRGBA *) dst->pixels;
2114 dgap = dst->pitch - dst->w * 4;
2117 for (y = 0; y < dst->h; y++)
2122 for (x = 0; x < dst->w; x++)
2127 /* advance source pointers */
2131 /* advance destination pointer */
2135 /* advance source pointer */
2137 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2139 /* advance destination pointers */
2140 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2150 -----------------------------------------------------------------------------
2153 zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2154 -----------------------------------------------------------------------------
2157 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2159 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2160 Uint8 *sp, *dp, *csp;
2163 /* variable setup */
2164 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2165 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2167 /* allocate memory for row increments */
2168 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2169 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2171 /* precalculate row increments */
2174 for (x = 0; x < dst->w; x++)
2177 *csax = (csx >> 16);
2184 for (y = 0; y < dst->h; y++)
2187 *csay = (csy >> 16);
2194 for (x = 0; x < dst->w; x++)
2202 for (y = 0; y < dst->h; y++)
2209 sp = csp = (Uint8 *) src->pixels;
2210 dp = (Uint8 *) dst->pixels;
2211 dgap = dst->pitch - dst->w;
2215 for (y = 0; y < dst->h; y++)
2219 for (x = 0; x < dst->w; x++)
2224 /* advance source pointers */
2228 /* advance destination pointer */
2232 /* advance source pointer (for row) */
2233 csp += ((*csay) * src->pitch);
2236 /* advance destination pointers */
2247 -----------------------------------------------------------------------------
2250 Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2251 'zoomx' and 'zoomy' are scaling factors for width and height.
2252 If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2253 into a 32bit RGBA format on the fly.
2254 -----------------------------------------------------------------------------
2257 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2259 SDL_Surface *zoom_src = NULL;
2260 SDL_Surface *zoom_dst = NULL;
2261 boolean is_converted = FALSE;
2268 /* determine if source surface is 32 bit or 8 bit */
2269 is_32bit = (src->format->BitsPerPixel == 32);
2271 if (is_32bit || src->format->BitsPerPixel == 8)
2273 /* use source surface 'as is' */
2278 /* new source surface is 32 bit with a defined RGB ordering */
2279 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2280 0x000000ff, 0x0000ff00, 0x00ff0000,
2281 (src->format->Amask ? 0xff000000 : 0));
2282 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2284 is_converted = TRUE;
2287 /* allocate surface to completely contain the zoomed surface */
2290 /* target surface is 32 bit with source RGBA/ABGR ordering */
2291 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2292 zoom_src->format->Rmask,
2293 zoom_src->format->Gmask,
2294 zoom_src->format->Bmask,
2295 zoom_src->format->Amask);
2299 /* target surface is 8 bit */
2300 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2304 /* lock source surface */
2305 SDL_LockSurface(zoom_src);
2307 /* check which kind of surface we have */
2310 /* call the 32 bit transformation routine to do the zooming */
2311 zoomSurfaceRGBA(zoom_src, zoom_dst);
2316 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2317 zoom_dst->format->palette->colors[i] =
2318 zoom_src->format->palette->colors[i];
2319 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2321 /* call the 8 bit transformation routine to do the zooming */
2322 zoomSurfaceY(zoom_src, zoom_dst);
2325 /* unlock source surface */
2326 SDL_UnlockSurface(zoom_src);
2328 /* free temporary surface */
2330 SDL_FreeSurface(zoom_src);
2332 /* return destination surface */
2336 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2338 SDL_Surface *new_surface;
2340 if (surface == NULL)
2343 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2344 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2346 /* remove alpha channel from native non-transparent surface, if defined */
2347 SDLSetAlpha(new_surface, FALSE, 0);
2349 /* remove transparent color from native non-transparent surface, if defined */
2350 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2355 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2357 Bitmap *dst_bitmap = CreateBitmapStruct();
2358 SDL_Surface *src_surface = src_bitmap->surface_masked;
2359 SDL_Surface *dst_surface;
2361 dst_width = MAX(1, dst_width); /* prevent zero bitmap width */
2362 dst_height = MAX(1, dst_height); /* prevent zero bitmap height */
2364 dst_bitmap->width = dst_width;
2365 dst_bitmap->height = dst_height;
2367 /* create zoomed temporary surface from source surface */
2368 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2370 /* create native format destination surface from zoomed temporary surface */
2371 SDLSetNativeSurface(&dst_surface);
2373 /* set color key for zoomed surface from source surface, if defined */
2374 if (SDLHasColorKey(src_surface))
2375 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2376 SDLGetColorKey(src_surface));
2378 /* create native non-transparent surface for opaque blitting */
2379 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2381 /* set native transparent surface for masked blitting */
2382 dst_bitmap->surface_masked = dst_surface;
2388 /* ========================================================================= */
2389 /* load image to bitmap */
2390 /* ========================================================================= */
2392 Bitmap *SDLLoadImage(char *filename)
2394 Bitmap *new_bitmap = CreateBitmapStruct();
2395 SDL_Surface *sdl_image_tmp;
2397 if (program.headless)
2399 /* prevent sanity check warnings at later stage */
2400 new_bitmap->width = new_bitmap->height = 1;
2405 print_timestamp_init("SDLLoadImage");
2407 print_timestamp_time(getBaseNamePtr(filename));
2409 /* load image to temporary surface */
2410 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2411 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
2413 print_timestamp_time("IMG_Load");
2415 UPDATE_BUSY_STATE();
2417 /* create native non-transparent surface for current image */
2418 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2419 Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2421 print_timestamp_time("SDLGetNativeSurface (opaque)");
2423 UPDATE_BUSY_STATE();
2425 /* set black pixel to transparent if no alpha channel / transparent color */
2426 if (!SDLHasAlpha(sdl_image_tmp) &&
2427 !SDLHasColorKey(sdl_image_tmp))
2428 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2429 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2431 /* create native transparent surface for current image */
2432 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2433 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2435 print_timestamp_time("SDLGetNativeSurface (masked)");
2437 UPDATE_BUSY_STATE();
2439 /* free temporary surface */
2440 SDL_FreeSurface(sdl_image_tmp);
2442 new_bitmap->width = new_bitmap->surface->w;
2443 new_bitmap->height = new_bitmap->surface->h;
2445 print_timestamp_done("SDLLoadImage");
2451 /* ------------------------------------------------------------------------- */
2452 /* custom cursor fuctions */
2453 /* ------------------------------------------------------------------------- */
2455 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2457 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2458 cursor_info->width, cursor_info->height,
2459 cursor_info->hot_x, cursor_info->hot_y);
2462 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2464 static struct MouseCursorInfo *last_cursor_info = NULL;
2465 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2466 static SDL_Cursor *cursor_default = NULL;
2467 static SDL_Cursor *cursor_current = NULL;
2469 /* if invoked for the first time, store the SDL default cursor */
2470 if (cursor_default == NULL)
2471 cursor_default = SDL_GetCursor();
2473 /* only create new cursor if cursor info (custom only) has changed */
2474 if (cursor_info != NULL && cursor_info != last_cursor_info)
2476 cursor_current = create_cursor(cursor_info);
2477 last_cursor_info = cursor_info;
2480 /* only set new cursor if cursor info (custom or NULL) has changed */
2481 if (cursor_info != last_cursor_info2)
2482 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2484 last_cursor_info2 = cursor_info;
2488 /* ========================================================================= */
2489 /* audio functions */
2490 /* ========================================================================= */
2492 void SDLOpenAudio(void)
2494 if (program.headless)
2497 #if !defined(TARGET_SDL2)
2498 if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
2499 SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
2502 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2504 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2508 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2509 AUDIO_NUM_CHANNELS_STEREO,
2510 setup.system.audio_fragment_size) < 0)
2512 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2516 audio.sound_available = TRUE;
2517 audio.music_available = TRUE;
2518 audio.loops_available = TRUE;
2519 audio.sound_enabled = TRUE;
2521 /* set number of available mixer channels */
2522 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2523 audio.music_channel = MUSIC_CHANNEL;
2524 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2526 Mixer_InitChannels();
2529 void SDLCloseAudio(void)
2532 Mix_HaltChannel(-1);
2535 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2539 /* ========================================================================= */
2540 /* event functions */
2541 /* ========================================================================= */
2543 void SDLWaitEvent(Event *event)
2545 SDL_WaitEvent(event);
2548 void SDLHandleWindowManagerEvent(Event *event)
2551 #if defined(PLATFORM_WIN32)
2552 // experimental drag and drop code
2554 SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
2555 SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
2557 #if defined(TARGET_SDL2)
2558 if (syswmmsg->msg.win.msg == WM_DROPFILES)
2560 if (syswmmsg->msg == WM_DROPFILES)
2563 #if defined(TARGET_SDL2)
2564 HDROP hdrop = (HDROP)syswmmsg->msg.win.wParam;
2566 HDROP hdrop = (HDROP)syswmmsg->wParam;
2570 printf("::: SDL_SYSWMEVENT:\n");
2572 num_files = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
2574 for (i = 0; i < num_files; i++)
2576 int buffer_len = DragQueryFile(hdrop, i, NULL, 0);
2577 char buffer[buffer_len + 1];
2579 DragQueryFile(hdrop, i, buffer, buffer_len + 1);
2581 printf("::: - '%s'\n", buffer);
2584 #if defined(TARGET_SDL2)
2585 DragFinish((HDROP)syswmmsg->msg.win.wParam);
2587 DragFinish((HDROP)syswmmsg->wParam);
2595 /* ========================================================================= */
2596 /* joystick functions */
2597 /* ========================================================================= */
2599 #if defined(TARGET_SDL2)
2600 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2602 static SDL_Joystick *sdl_joystick[MAX_PLAYERS]; // only joysticks supported
2604 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2605 static int sdl_js_axis[MAX_PLAYERS][2];
2606 static int sdl_js_button[MAX_PLAYERS][2];
2607 static boolean sdl_is_controller[MAX_PLAYERS];
2609 void SDLClearJoystickState()
2613 for (i = 0; i < MAX_PLAYERS; i++)
2615 for (j = 0; j < 2; j++)
2617 sdl_js_axis_raw[i][j] = -1;
2618 sdl_js_axis[i][j] = 0;
2619 sdl_js_button[i][j] = 0;
2624 boolean SDLOpenJoystick(int nr)
2626 if (nr < 0 || nr >= MAX_PLAYERS)
2629 #if defined(TARGET_SDL2)
2630 sdl_is_controller[nr] = SDL_IsGameController(nr);
2632 sdl_is_controller[nr] = FALSE;
2636 Error(ERR_DEBUG, "opening joystick %d (%s)",
2637 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2640 #if defined(TARGET_SDL2)
2641 if (sdl_is_controller[nr])
2642 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2644 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2646 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2649 return (sdl_joystick[nr] != NULL);
2652 void SDLCloseJoystick(int nr)
2654 if (nr < 0 || nr >= MAX_PLAYERS)
2658 Error(ERR_DEBUG, "closing joystick %d", nr);
2661 #if defined(TARGET_SDL2)
2662 if (sdl_is_controller[nr])
2663 SDL_GameControllerClose(sdl_joystick[nr]);
2665 SDL_JoystickClose(sdl_joystick[nr]);
2667 SDL_JoystickClose(sdl_joystick[nr]);
2670 sdl_joystick[nr] = NULL;
2673 boolean SDLCheckJoystickOpened(int nr)
2675 if (nr < 0 || nr >= MAX_PLAYERS)
2678 #if defined(TARGET_SDL2)
2679 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2681 return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
2685 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2687 #if defined(TARGET_SDL2)
2688 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2689 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2690 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2691 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2693 int axis_id = axis_id_raw % 2;
2696 if (nr < 0 || nr >= MAX_PLAYERS)
2702 // prevent (slightly jittering, but centered) axis A from resetting axis B
2703 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2704 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2707 sdl_js_axis[nr][axis_id] = axis_value;
2708 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2711 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2713 #if defined(TARGET_SDL2)
2714 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2715 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2716 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2717 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2718 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2719 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2720 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2721 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2724 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2725 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2726 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2727 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2728 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2729 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2730 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2731 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2733 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2734 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2735 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2736 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2737 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2739 int button_id = button_id_raw % 2;
2742 if (nr < 0 || nr >= MAX_PLAYERS)
2745 if (button_id == -1)
2748 sdl_js_button[nr][button_id] = button_state;
2751 void HandleJoystickEvent(Event *event)
2755 #if defined(TARGET_SDL2)
2756 case SDL_CONTROLLERDEVICEADDED:
2758 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2759 event->cdevice.which);
2764 case SDL_CONTROLLERDEVICEREMOVED:
2766 Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2767 event->cdevice.which);
2772 case SDL_CONTROLLERAXISMOTION:
2774 Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2775 event->caxis.which, event->caxis.axis, event->caxis.value);
2777 setJoystickAxis(event->caxis.which,
2779 event->caxis.value);
2782 case SDL_CONTROLLERBUTTONDOWN:
2784 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2785 event->cbutton.which, event->cbutton.button);
2787 setJoystickButton(event->cbutton.which,
2788 event->cbutton.button,
2792 case SDL_CONTROLLERBUTTONUP:
2794 Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2795 event->cbutton.which, event->cbutton.button);
2797 setJoystickButton(event->cbutton.which,
2798 event->cbutton.button,
2803 case SDL_JOYAXISMOTION:
2804 if (sdl_is_controller[event->jaxis.which])
2808 Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2809 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2811 if (event->jaxis.axis < 4)
2812 setJoystickAxis(event->jaxis.which,
2814 event->jaxis.value);
2817 case SDL_JOYBUTTONDOWN:
2818 if (sdl_is_controller[event->jaxis.which])
2822 Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2823 event->jbutton.which, event->jbutton.button);
2825 if (event->jbutton.button < 4)
2826 setJoystickButton(event->jbutton.which,
2827 event->jbutton.button,
2831 case SDL_JOYBUTTONUP:
2832 if (sdl_is_controller[event->jaxis.which])
2836 Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2837 event->jbutton.which, event->jbutton.button);
2839 if (event->jbutton.button < 4)
2840 setJoystickButton(event->jbutton.which,
2841 event->jbutton.button,
2850 void SDLInitJoysticks()
2852 static boolean sdl_joystick_subsystem_initialized = FALSE;
2853 boolean print_warning = !sdl_joystick_subsystem_initialized;
2854 #if defined(TARGET_SDL2)
2855 char *mappings_file_base = getPath2(options.conf_directory,
2856 GAMECONTROLLER_BASENAME);
2857 char *mappings_file_user = getPath2(getUserGameDataDir(),
2858 GAMECONTROLLER_BASENAME);
2863 if (!sdl_joystick_subsystem_initialized)
2865 sdl_joystick_subsystem_initialized = TRUE;
2867 #if defined(TARGET_SDL2)
2868 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2870 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2872 if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
2875 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2879 #if defined(TARGET_SDL2)
2880 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2882 /* the included game controller base mappings should always be found */
2883 if (num_mappings == -1)
2884 Error(ERR_WARN, "no game controller base mappings found");
2887 Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2890 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2893 /* the personal game controller user mappings may or may not be found */
2894 if (num_mappings == -1)
2895 Error(ERR_WARN, "no game controller user mappings found");
2897 Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2899 Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2902 checked_free(mappings_file_base);
2903 checked_free(mappings_file_user);
2906 for (i = 0; i < SDL_NumJoysticks(); i++)
2908 const char *name, *type;
2910 if (SDL_IsGameController(i))
2912 name = SDL_GameControllerNameForIndex(i);
2913 type = "game controller";
2917 name = SDL_JoystickNameForIndex(i);
2921 Error(ERR_INFO, "- joystick %d (%s): '%s'",
2922 i, type, (name ? name : "(Unknown)"));
2928 /* assign joysticks from configured to connected joystick for all players */
2929 for (i = 0; i < MAX_PLAYERS; i++)
2931 /* get configured joystick for this player */
2932 char *device_name = setup.input[i].joy.device_name;
2933 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2935 if (joystick_nr >= SDL_NumJoysticks())
2937 if (setup.input[i].use_joystick && print_warning)
2938 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2943 /* store configured joystick number for each player */
2944 joystick.nr[i] = joystick_nr;
2947 /* now open all connected joysticks (regardless if configured or not) */
2948 for (i = 0; i < SDL_NumJoysticks(); i++)
2950 /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
2951 if (SDLCheckJoystickOpened(i))
2952 SDLCloseJoystick(i);
2954 if (SDLOpenJoystick(i))
2955 joystick.status = JOYSTICK_ACTIVATED;
2956 else if (print_warning)
2957 Error(ERR_WARN, "cannot open joystick %d", i);
2960 SDLClearJoystickState();
2963 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2965 if (nr < 0 || nr >= MAX_PLAYERS)
2969 *x = sdl_js_axis[nr][0];
2971 *y = sdl_js_axis[nr][1];
2974 *b1 = sdl_js_button[nr][0];
2976 *b2 = sdl_js_button[nr][1];
2982 /* ========================================================================= */
2983 /* touch input overlay functions */
2984 /* ========================================================================= */
2986 #if defined(USE_TOUCH_INPUT_OVERLAY)
2987 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2990 int grid_xsize = overlay.grid_xsize;
2991 int grid_ysize = overlay.grid_ysize;
2994 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2995 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2997 for (x = 0; x < grid_xsize; x++)
2999 rect.x = (x + 0) * video.screen_width / grid_xsize;
3000 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
3002 for (y = 0; y < grid_ysize; y++)
3004 rect.y = (y + 0) * video.screen_height / grid_ysize;
3005 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
3007 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
3008 SDL_RenderDrawRect(sdl_renderer, &rect);
3012 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3015 static void RenderFillRectangle(int x, int y, int width, int height)
3017 SDL_Rect rect = { x, y, width, height };
3019 SDL_RenderFillRect(sdl_renderer, &rect);
3022 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
3024 static int alpha_direction = 0;
3025 static int alpha_highlight = 0;
3026 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3027 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3029 int grid_xsize = overlay.grid_xsize;
3030 int grid_ysize = overlay.grid_ysize;
3033 if (alpha == alpha_max)
3035 if (alpha_direction < 0)
3037 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
3039 if (alpha_highlight == 0)
3040 alpha_direction = 1;
3044 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
3046 if (alpha_highlight == alpha_max)
3047 alpha_direction = -1;
3052 alpha_direction = 1;
3053 alpha_highlight = alpha;
3056 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
3058 for (x = 0; x < grid_xsize; x++)
3060 for (y = 0; y < grid_ysize; y++)
3062 int grid_button = overlay.grid_button[x][y];
3063 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
3064 int alpha_draw = alpha;
3065 int outline_border = MV_NONE;
3066 int border_size = 2;
3067 boolean draw_outlined = setup.touch.draw_outlined;
3068 boolean draw_pressed = setup.touch.draw_pressed;
3070 if (grid_button == CHAR_GRID_BUTTON_NONE)
3073 if (grid_button == overlay.grid_button_highlight)
3074 alpha_draw = alpha_highlight;
3076 if (draw_pressed && overlay.grid_button_action & grid_button_action)
3079 draw_outlined = FALSE;
3081 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
3084 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
3086 rect.x = (x + 0) * video.screen_width / grid_xsize;
3087 rect.y = (y + 0) * video.screen_height / grid_ysize;
3088 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
3089 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
3091 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
3093 rect.x += border_size;
3094 rect.w -= border_size;
3096 outline_border |= MV_LEFT;
3099 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
3101 rect.w -= border_size;
3103 outline_border |= MV_RIGHT;
3106 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
3108 rect.y += border_size;
3109 rect.h -= border_size;
3111 outline_border |= MV_UP;
3114 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
3116 rect.h -= border_size;
3118 outline_border |= MV_DOWN;
3123 int rect_x = rect.x +
3124 (outline_border & MV_LEFT ? border_size : 0);
3125 int rect_w = rect.w -
3126 (outline_border & MV_LEFT ? border_size : 0) -
3127 (outline_border & MV_RIGHT ? border_size : 0);
3129 if (outline_border & MV_LEFT)
3130 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
3132 if (outline_border & MV_RIGHT)
3133 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
3134 border_size, rect.h);
3136 if (outline_border & MV_UP)
3137 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
3139 if (outline_border & MV_DOWN)
3140 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
3141 rect_w, border_size);
3145 SDL_RenderFillRect(sdl_renderer, &rect);
3150 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3153 static void DrawTouchInputOverlay()
3155 static SDL_Texture *texture = NULL;
3156 static boolean initialized = FALSE;
3157 static boolean deactivated = TRUE;
3158 static boolean show_grid = FALSE;
3159 static int width = 0, height = 0;
3160 static int alpha_last = -1;
3161 static int alpha = 0;
3162 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3163 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3164 boolean active = (overlay.enabled && overlay.active);
3166 if (!active && deactivated)
3171 if (alpha < alpha_max)
3172 alpha = MIN(alpha + alpha_step, alpha_max);
3174 deactivated = FALSE;
3178 alpha = MAX(0, alpha - alpha_step);
3184 if (overlay.show_grid)
3186 else if (deactivated)
3190 DrawTouchInputOverlay_ShowGrid(alpha);
3192 DrawTouchInputOverlay_ShowGridButtons(alpha);
3197 // !!! VIRTUAL BUTTONS FROM IMAGE FILE NOT USED ANYMORE !!!
3201 char *basename = "overlay/VirtualButtons.png";
3202 char *filename = getCustomImageFilename(basename);
3204 if (filename == NULL)
3205 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
3207 SDL_Surface *surface;
3209 if ((surface = IMG_Load(filename)) == NULL)
3210 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
3213 height = surface->h;
3215 /* set black pixel to transparent if no alpha channel / transparent color */
3216 if (!SDLHasAlpha(surface) &&
3217 !SDLHasColorKey(surface))
3218 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
3219 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
3221 if ((texture = SDLCreateTextureFromSurface(surface)) == NULL)
3222 Error(ERR_EXIT, "SDLCreateTextureFromSurface() failed");
3224 SDL_FreeSurface(surface);
3226 SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
3231 if (alpha != alpha_last)
3232 SDL_SetTextureAlphaMod(texture, alpha);
3236 float ratio_overlay = (float) width / height;
3237 float ratio_screen = (float) video.screen_width / video.screen_height;
3238 int width_scaled, height_scaled;
3241 if (ratio_overlay > ratio_screen)
3243 width_scaled = video.screen_width;
3244 height_scaled = video.screen_height * ratio_screen / ratio_overlay;
3246 ypos = video.screen_height - height_scaled;
3250 width_scaled = video.screen_width * ratio_overlay / ratio_screen;
3251 height_scaled = video.screen_height;
3252 xpos = (video.screen_width - width_scaled) / 2;
3256 SDL_Rect src_rect = { 0, 0, width, height };
3257 SDL_Rect dst_rect = { xpos, ypos, width_scaled, height_scaled };
3259 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);