1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
19 #define ENABLE_UNUSED_CODE 0 // currently unused functions
21 #define DEBUG_JOYSTICKS 0
24 // ============================================================================
26 // ============================================================================
28 // SDL internal variables
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;
34 static boolean limit_screen_updates = FALSE;
37 // functions from SGE library
38 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
40 #if defined(USE_TOUCH_INPUT_OVERLAY)
41 // functions to draw overlay graphics for touch device input
42 static void DrawTouchInputOverlay(void);
43 static void DrawTouchGadgetsOverlay(void);
46 void SDLLimitScreenUpdates(boolean enable)
48 limit_screen_updates = enable;
51 static void FinalizeScreen(int draw_target)
53 // copy global animations to render target buffer, if defined (below border)
54 if (gfx.draw_global_anim_function != NULL)
55 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
57 // copy global masked border to render target buffer, if defined
58 if (gfx.draw_global_border_function != NULL)
59 gfx.draw_global_border_function(draw_target);
61 // copy global animations to render target buffer, if defined (above border)
62 if (gfx.draw_global_anim_function != NULL)
63 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
65 // copy tile selection cursor to render target buffer, if defined (above all)
66 if (gfx.draw_tile_cursor_function != NULL)
67 gfx.draw_tile_cursor_function(draw_target);
70 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
72 static DelayCounter update_screen_delay = { 50 }; // (milliseconds)
73 SDL_Surface *screen = backbuffer->surface;
75 if (limit_screen_updates &&
76 !DelayReached(&update_screen_delay))
79 LimitScreenUpdates(FALSE);
83 static int LastFrameCounter = 0;
84 boolean changed = (FrameCounter != LastFrameCounter);
86 Debug("internal:frame", "FrameCounter == %d [%s]", FrameCounter,
87 (changed ? "-" : "SAME FRAME UPDATED"));
89 LastFrameCounter = FrameCounter;
98 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
99 gfx.final_screen_bitmap != NULL) // may not be initialized yet
101 // draw global animations using bitmaps instead of using textures
102 // to prevent texture scaling artefacts (this is potentially slower)
104 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
105 gfx.win_xsize, gfx.win_ysize, 0, 0);
107 FinalizeScreen(DRAW_TO_SCREEN);
109 screen = gfx.final_screen_bitmap->surface;
111 // force full window redraw
115 SDL_Texture *sdl_texture = sdl_texture_stream;
117 // deactivate use of target texture if render targets are not supported
118 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
119 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
120 sdl_texture_target == NULL)
121 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
123 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
124 sdl_texture = sdl_texture_target;
128 int bytes_x = screen->pitch / video.width;
129 int bytes_y = screen->pitch;
131 SDL_UpdateTexture(sdl_texture, rect,
132 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
137 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
140 int xoff = video.screen_xoffset;
141 int yoff = video.screen_yoffset;
142 SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
143 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
144 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
146 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
147 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
148 dst_rect2 = &dst_rect_screen;
150 dst_rect1 = &dst_rect_screen;
152 #if defined(HAS_SCREEN_KEYBOARD)
153 SDL_Rect src_rect_up = { 0, 0, video.width, video.height };
154 SDL_Rect dst_rect_up = dst_rect_screen;
156 if (video.shifted_up || video.shifted_up_delay.count)
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, time_current))
164 int delay_count = time_current - video.shifted_up_delay.count;
165 int delay_value = video.shifted_up_delay.value;
167 pos = pos_last + (pos - pos_last) * delay_count / delay_value;
171 video.shifted_up_pos_last = pos;
172 video.shifted_up_delay.count = 0;
176 src_rect_up.h = video.height - pos;
177 dst_rect_up.h = 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();
220 // draw overlay gadgets for touch device input, if needed
221 DrawTouchGadgetsOverlay();
224 // global synchronization point of the game to align video frame delay
225 if (with_frame_delay)
226 WaitUntilDelayReached(&video.frame_delay);
228 video.frame_counter++;
230 // show render target buffer on screen
231 SDL_RenderPresent(sdl_renderer);
234 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
236 PumpEvents(); // execute event filter actions while waiting
238 UpdateScreenExt(rect, TRUE);
241 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
243 UpdateScreenExt(rect, FALSE);
246 void Delay_WithScreenUpdates(unsigned int delay)
248 unsigned int time_current = SDL_GetTicks();
249 unsigned int time_delayed = time_current + delay;
251 while (time_current < time_delayed)
253 // updating the screen contains waiting for frame delay (non-busy)
254 UpdateScreen_WithFrameDelay(NULL);
256 time_current = SDL_GetTicks();
260 static void SDLSetWindowIcon(char *basename)
262 // (setting the window icon on Mac OS X would replace the high-quality
263 // dock icon with the currently smaller (and uglier) icon from file)
265 #if !defined(PLATFORM_MAC)
266 char *filename = getCustomImageFilename(basename);
267 SDL_Surface *surface;
269 if (filename == NULL)
271 Warn("SDLSetWindowIcon(): cannot find file '%s'", basename);
276 if ((surface = IMG_Load(filename)) == NULL)
278 Warn("IMG_Load('%s') failed: %s", basename, SDL_GetError());
283 // set transparent color
284 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
285 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
287 SDL_SetWindowIcon(sdl_window, surface);
291 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
292 SDL_PixelFormat *format2)
294 return (format1->format == format2->format &&
295 format1->BitsPerPixel == format2->BitsPerPixel &&
296 format1->BytesPerPixel == format2->BytesPerPixel &&
297 format1->Rmask == format2->Rmask &&
298 format1->Gmask == format2->Gmask &&
299 format1->Bmask == format2->Bmask);
302 static void SDLCopyColorKey(SDL_Surface *src_surface, SDL_Surface *dst_surface)
307 // check if source surface has a color key
308 if (SDL_GetColorKey(src_surface, &color_key) == 0)
310 // get RGB values of color key of source surface
311 SDL_GetRGB(color_key, src_surface->format, &r, &g, &b);
313 // get color key from RGB values in destination surface format
314 color_key = SDL_MapRGB(dst_surface->format, r, g, b);
316 // set color key in destination surface
317 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL, color_key);
321 // unset color key in destination surface
322 SDL_SetColorKey(dst_surface, UNSET_TRANSPARENT_PIXEL, 0);
326 static boolean SDLHasColorKey(SDL_Surface *surface)
330 return (SDL_GetColorKey(surface, &color_key) == 0);
333 static boolean SDLHasAlpha(SDL_Surface *surface)
335 SDL_BlendMode blend_mode;
337 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
340 return (blend_mode == SDL_BLENDMODE_BLEND);
343 void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
345 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
347 SDL_SetSurfaceBlendMode(surface, blend_mode);
348 SDL_SetSurfaceAlphaMod(surface, alpha);
351 const char *SDLGetRendererName(void)
353 static SDL_RendererInfo renderer_info;
355 SDL_GetRendererInfo(sdl_renderer, &renderer_info);
357 return renderer_info.name;
360 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
362 SDL_PixelFormat format;
363 SDL_Surface *new_surface;
368 if (backbuffer && backbuffer->surface)
370 format = *backbuffer->surface->format;
371 format.Amask = surface->format->Amask; // keep alpha channel
375 format = *surface->format;
378 new_surface = SDL_ConvertSurface(surface, &format, 0);
380 if (new_surface == NULL)
381 Fail("SDL_ConvertSurface() failed: %s", SDL_GetError());
383 // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
384 if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
385 SDLCopyColorKey(surface, new_surface);
390 boolean SDLSetNativeSurface(SDL_Surface **surface)
392 SDL_Surface *new_surface;
394 if (surface == NULL ||
396 backbuffer == NULL ||
397 backbuffer->surface == NULL)
400 // if pixel format already optimized for destination surface, do nothing
401 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
404 new_surface = SDLGetNativeSurface(*surface);
406 SDL_FreeSurface(*surface);
408 *surface = new_surface;
413 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
415 if (program.headless)
418 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
421 Fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
426 void SDLCreateBitmapTextures(Bitmap *bitmap)
432 SDL_DestroyTexture(bitmap->texture);
433 if (bitmap->texture_masked)
434 SDL_DestroyTexture(bitmap->texture_masked);
436 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
437 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
440 void SDLFreeBitmapTextures(Bitmap *bitmap)
446 SDL_DestroyTexture(bitmap->texture);
447 if (bitmap->texture_masked)
448 SDL_DestroyTexture(bitmap->texture_masked);
450 bitmap->texture = NULL;
451 bitmap->texture_masked = NULL;
454 void SDLInitVideoDisplay(void)
456 // set hint to select render driver as specified in setup config file
457 if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
458 SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
460 // initialize SDL video
461 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
462 Fail("SDL_InitSubSystem() failed: %s", SDL_GetError());
464 // set default SDL depth
465 video.default_depth = 32; // (how to determine video depth in SDL2?)
467 // Code used with SDL 1.2:
468 // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
471 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
473 if (program.headless)
476 video.window_scaling_percent = setup.window_scaling_percent;
477 video.window_scaling_quality = setup.window_scaling_quality;
479 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
481 // SDL 2.0: support for (desktop) fullscreen mode available
482 video.fullscreen_available = TRUE;
484 // open SDL video output device (window or fullscreen mode)
485 if (!SDLSetVideoMode(fullscreen))
486 Fail("setting video mode failed");
488 // !!! SDL2 can only set the window icon if the window already exists !!!
490 SDLSetWindowIcon(program.icon_filename);
492 // set window and icon title
496 static void SDLInitVideoBuffer_DrawBuffer(void)
498 /* SDL cannot directly draw to the visible video framebuffer like X11,
499 but always uses a backbuffer, which is then blitted to the visible
500 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
501 visible video framebuffer with 'SDL_Flip', if the hardware supports
502 this). Therefore do not use an additional backbuffer for drawing, but
503 use a symbolic buffer (distinguishable from the SDL backbuffer) called
504 'window', which indicates that the SDL backbuffer should be updated to
505 the visible video framebuffer when attempting to blit to it.
507 For convenience, it seems to be a good idea to create this symbolic
508 buffer 'window' at the same size as the SDL backbuffer. Although it
509 should never be drawn to directly, it would do no harm nevertheless. */
511 // create additional (symbolic) buffer for double-buffering
512 ReCreateBitmap(&window, video.width, video.height);
514 // create dummy drawing buffer for headless mode, if needed
515 if (program.headless)
516 ReCreateBitmap(&backbuffer, video.width, video.height);
519 void SDLInitVideoBuffer(boolean fullscreen)
521 SDLInitVideoBuffer_VideoBuffer(fullscreen);
522 SDLInitVideoBuffer_DrawBuffer();
525 static boolean SDLCreateScreen(boolean fullscreen)
527 SDL_Surface *new_surface = NULL;
529 int surface_flags_window = SURFACE_FLAGS;
530 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
533 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
535 video.vsync_mode = VSYNC_MODE_OFF;
537 if (!strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF))
539 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
540 video.vsync_mode = VSYNC_MODE_NORMAL;
543 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
544 _without_ enabling 2D/3D acceleration and/or guest additions installed,
545 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
546 it will try to use accelerated graphics and apparently fails miserably) */
547 int renderer_flags = SDL_RENDERER_SOFTWARE;
550 int width = video.width;
551 int height = video.height;
552 int screen_width = video.screen_width;
553 int screen_height = video.screen_height;
554 int surface_flags = (fullscreen ? surface_flags_fullscreen :
555 surface_flags_window);
557 // default window size is unscaled
558 video.window_width = screen_width;
559 video.window_height = screen_height;
561 // store if initial screen mode is fullscreen mode when changing screen size
562 video.fullscreen_initial = fullscreen;
564 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
566 video.window_width = window_scaling_factor * screen_width;
567 video.window_height = window_scaling_factor * screen_height;
569 if (sdl_texture_stream)
571 SDL_DestroyTexture(sdl_texture_stream);
572 sdl_texture_stream = NULL;
575 if (sdl_texture_target)
577 SDL_DestroyTexture(sdl_texture_target);
578 sdl_texture_target = NULL;
581 if (!(fullscreen && fullscreen_enabled))
585 SDL_DestroyRenderer(sdl_renderer);
591 SDL_DestroyWindow(sdl_window);
596 if (sdl_window == NULL)
597 sdl_window = SDL_CreateWindow(program.window_title,
598 SDL_WINDOWPOS_CENTERED,
599 SDL_WINDOWPOS_CENTERED,
604 if (sdl_window != NULL)
606 if (sdl_renderer == NULL)
607 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
609 if (sdl_renderer != NULL)
611 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
612 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
614 // required for setting adaptive vsync when using OpenGL renderer
615 SDLSetScreenVsyncMode(setup.vsync_mode);
617 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
618 SDL_PIXELFORMAT_ARGB8888,
619 SDL_TEXTUREACCESS_STREAMING,
622 if (SDL_RenderTargetSupported(sdl_renderer))
623 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
624 SDL_PIXELFORMAT_ARGB8888,
625 SDL_TEXTUREACCESS_TARGET,
628 if (sdl_texture_stream != NULL)
630 // use SDL default values for RGB masks and no alpha channel
631 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
633 if (new_surface == NULL)
634 Warn("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
638 Warn("SDL_CreateTexture() failed: %s", SDL_GetError());
643 Warn("SDL_CreateRenderer() failed: %s", SDL_GetError());
648 Warn("SDL_CreateWindow() failed: %s", SDL_GetError());
651 SDLSetScreenProperties();
653 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
654 if (new_surface != NULL)
655 fullscreen_enabled = fullscreen;
657 if (backbuffer == NULL)
658 backbuffer = CreateBitmapStruct();
660 backbuffer->width = video.width;
661 backbuffer->height = video.height;
663 if (backbuffer->surface)
664 SDL_FreeSurface(backbuffer->surface);
666 backbuffer->surface = new_surface;
668 return (new_surface != NULL);
671 boolean SDLSetVideoMode(boolean fullscreen)
673 boolean success = FALSE;
677 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
679 // switch display to fullscreen mode, if available
680 success = SDLCreateScreen(TRUE);
684 // switching display to fullscreen mode failed -- do not try it again
685 video.fullscreen_available = FALSE;
689 video.fullscreen_enabled = TRUE;
693 if ((!fullscreen && video.fullscreen_enabled) || !success)
695 // switch display to window mode
696 success = SDLCreateScreen(FALSE);
700 // switching display to window mode failed -- should not happen
704 video.fullscreen_enabled = FALSE;
705 video.window_scaling_percent = setup.window_scaling_percent;
706 video.window_scaling_quality = setup.window_scaling_quality;
708 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
712 SDLRedrawWindow(); // map window
717 void SDLSetWindowTitle(void)
719 if (sdl_window == NULL)
722 SDL_SetWindowTitle(sdl_window, program.window_title);
725 void SDLSetWindowScaling(int window_scaling_percent)
727 if (sdl_window == NULL)
730 float window_scaling_factor = (float)window_scaling_percent / 100;
731 int new_window_width = (int)(window_scaling_factor * video.screen_width);
732 int new_window_height = (int)(window_scaling_factor * video.screen_height);
734 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
736 video.window_scaling_percent = window_scaling_percent;
737 video.window_width = new_window_width;
738 video.window_height = new_window_height;
743 void SDLSetWindowScalingQuality(char *window_scaling_quality)
745 SDL_Texture *new_texture;
747 if (sdl_texture_stream == NULL)
750 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
752 new_texture = SDL_CreateTexture(sdl_renderer,
753 SDL_PIXELFORMAT_ARGB8888,
754 SDL_TEXTUREACCESS_STREAMING,
755 video.width, video.height);
757 if (new_texture != NULL)
759 SDL_DestroyTexture(sdl_texture_stream);
761 sdl_texture_stream = new_texture;
764 if (SDL_RenderTargetSupported(sdl_renderer))
765 new_texture = SDL_CreateTexture(sdl_renderer,
766 SDL_PIXELFORMAT_ARGB8888,
767 SDL_TEXTUREACCESS_TARGET,
768 video.width, video.height);
772 if (new_texture != NULL)
774 SDL_DestroyTexture(sdl_texture_target);
776 sdl_texture_target = new_texture;
781 video.window_scaling_quality = window_scaling_quality;
784 void SDLSetWindowFullscreen(boolean fullscreen)
786 if (sdl_window == NULL)
789 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
791 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
792 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
794 // if screen size was changed in fullscreen mode, correct desktop window size
795 if (!fullscreen && video.fullscreen_initial)
797 SDLSetWindowScaling(setup.window_scaling_percent);
798 SDL_SetWindowPosition(sdl_window,
799 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
801 video.fullscreen_initial = FALSE;
805 void SDLSetDisplaySize(void)
807 if (sdl_renderer != NULL)
811 SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
813 video.display_width = w;
814 video.display_height = h;
817 Debug("video", "SDL renderer size: %d x %d",
818 video.display_width, video.display_height);
823 SDL_Rect display_bounds;
825 SDL_GetDisplayBounds(0, &display_bounds);
827 video.display_width = display_bounds.w;
828 video.display_height = display_bounds.h;
831 Debug("video", "SDL display size: %d x %d",
832 video.display_width, video.display_height);
837 void SDLSetScreenSizeAndOffsets(int width, int height)
839 // set default video screen size and offsets
840 video.screen_width = width;
841 video.screen_height = height;
842 video.screen_xoffset = 0;
843 video.screen_yoffset = 0;
845 #if defined(USE_COMPLETE_DISPLAY)
846 float ratio_video = (float) width / height;
847 float ratio_display = (float) video.display_width / video.display_height;
849 if (ratio_video != ratio_display)
851 // adjust drawable screen size to cover the whole device display
853 if (ratio_video < ratio_display)
854 video.screen_width *= ratio_display / ratio_video;
856 video.screen_height *= ratio_video / ratio_display;
858 video.screen_xoffset = (video.screen_width - width) / 2;
859 video.screen_yoffset = (video.screen_height - height) / 2;
862 Debug("video", "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
864 video.screen_width, video.screen_height,
865 ratio_video, ratio_display);
871 void SDLSetScreenSizeForRenderer(int width, int height)
873 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
876 void SDLSetScreenProperties(void)
879 SDLSetScreenSizeAndOffsets(video.width, video.height);
880 SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
882 SetOverlayGridSizeAndButtons();
885 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
887 video.screen_rendering_mode =
888 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
889 SPECIAL_RENDERING_BITMAP :
890 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
891 SPECIAL_RENDERING_TARGET:
892 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
893 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
896 void SDLSetScreenVsyncMode(char *vsync_mode)
898 // changing vsync mode without re-creating renderer only supported by OpenGL
899 if (!strPrefixLower((char *)SDLGetRendererName(), "opengl"))
902 int interval = VSYNC_MODE_STR_TO_INT(vsync_mode);
903 int result = SDL_GL_SetSwapInterval(interval);
905 // if adaptive vsync requested, but not supported, retry with normal vsync
906 if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
908 interval = VSYNC_MODE_NORMAL;
910 result = SDL_GL_SetSwapInterval(interval);
914 interval = VSYNC_MODE_OFF;
916 video.vsync_mode = interval;
919 void SDLRedrawWindow(void)
921 UpdateScreen_WithoutFrameDelay(NULL);
924 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
927 if (program.headless)
930 SDL_Surface *surface =
931 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
934 Fail("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
936 SDLSetNativeSurface(&surface);
938 bitmap->surface = surface;
941 void SDLFreeBitmapPointers(Bitmap *bitmap)
944 SDL_FreeSurface(bitmap->surface);
945 if (bitmap->surface_masked)
946 SDL_FreeSurface(bitmap->surface_masked);
948 bitmap->surface = NULL;
949 bitmap->surface_masked = NULL;
952 SDL_DestroyTexture(bitmap->texture);
953 if (bitmap->texture_masked)
954 SDL_DestroyTexture(bitmap->texture_masked);
956 bitmap->texture = NULL;
957 bitmap->texture_masked = NULL;
960 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
961 int src_x, int src_y, int width, int height,
962 int dst_x, int dst_y, int mask_mode)
964 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
965 SDL_Rect src_rect, dst_rect;
977 // if (src_bitmap != backbuffer || dst_bitmap != window)
978 if (!(src_bitmap == backbuffer && dst_bitmap == window))
979 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
980 src_bitmap->surface_masked : src_bitmap->surface),
981 &src_rect, real_dst_bitmap->surface, &dst_rect);
983 if (dst_bitmap == window)
984 UpdateScreen_WithFrameDelay(&dst_rect);
987 void SDLBlitTexture(Bitmap *bitmap,
988 int src_x, int src_y, int width, int height,
989 int dst_x, int dst_y, int mask_mode)
991 SDL_Texture *texture;
996 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1004 src_rect.h = height;
1009 dst_rect.h = height;
1011 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1014 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1017 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1025 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1027 if (dst_bitmap == window)
1028 UpdateScreen_WithFrameDelay(&rect);
1031 void PrepareFadeBitmap(int draw_target)
1033 Bitmap *fade_bitmap =
1034 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1035 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1037 if (fade_bitmap == NULL)
1040 // copy backbuffer to fading buffer
1041 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1043 // add border and animations to fading buffer
1044 FinalizeScreen(draw_target);
1047 void SDLFadeRectangle(int x, int y, int width, int height,
1048 int fade_mode, int fade_delay, int post_delay,
1049 void (*draw_border_function)(void))
1051 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1052 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1053 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1054 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1055 SDL_Surface *surface_screen = backbuffer->surface;
1056 SDL_Rect src_rect, dst_rect;
1058 int src_x = x, src_y = y;
1059 int dst_x = x, dst_y = y;
1060 unsigned int time_last, time_current;
1062 // store function for drawing global masked border
1063 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1065 // deactivate drawing of global border while fading, if needed
1066 if (draw_border_function == NULL)
1067 gfx.draw_global_border_function = NULL;
1072 src_rect.h = height;
1076 dst_rect.w = width; // (ignored)
1077 dst_rect.h = height; // (ignored)
1079 dst_rect2 = dst_rect;
1081 // before fading in, store backbuffer (without animation graphics)
1082 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1083 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1085 // copy source and target surfaces to temporary surfaces for fading
1086 if (fade_mode & FADE_TYPE_TRANSFORM)
1088 // (source and target fading buffer already prepared)
1090 else if (fade_mode & FADE_TYPE_FADE_IN)
1092 // (target fading buffer already prepared)
1093 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1095 else // FADE_TYPE_FADE_OUT
1097 // (source fading buffer already prepared)
1098 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1101 time_current = SDL_GetTicks();
1103 if (fade_delay <= 0)
1105 // immediately draw final target frame without delay
1106 fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1110 // when fading without delay, also skip post delay
1114 if (fade_mode == FADE_MODE_MELT)
1116 boolean done = FALSE;
1117 int melt_pixels = 2;
1118 int melt_columns = width / melt_pixels;
1119 int ypos[melt_columns];
1120 int max_steps = height / 8 + 32;
1125 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1127 SDLSetAlpha(surface_target, FALSE, 0); // disable alpha blending
1129 ypos[0] = -GetSimpleRandom(16);
1131 for (i = 1 ; i < melt_columns; i++)
1133 int r = GetSimpleRandom(3) - 1; // randomly choose from { -1, 0, -1 }
1135 ypos[i] = ypos[i - 1] + r;
1148 time_last = time_current;
1149 time_current = SDL_GetTicks();
1150 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1151 steps_final = MIN(MAX(0, steps), max_steps);
1155 done = (steps_done >= steps_final);
1157 for (i = 0 ; i < melt_columns; i++)
1165 else if (ypos[i] < height)
1170 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1172 if (ypos[i] + dy >= height)
1173 dy = height - ypos[i];
1175 // copy part of (appearing) target surface to upper area
1176 src_rect.x = src_x + i * melt_pixels;
1177 // src_rect.y = src_y + ypos[i];
1179 src_rect.w = melt_pixels;
1181 src_rect.h = ypos[i] + dy;
1183 dst_rect.x = dst_x + i * melt_pixels;
1184 // dst_rect.y = dst_y + ypos[i];
1187 if (steps_done >= steps_final)
1188 SDL_BlitSurface(surface_target, &src_rect,
1189 surface_screen, &dst_rect);
1193 // copy part of (disappearing) source surface to lower area
1194 src_rect.x = src_x + i * melt_pixels;
1196 src_rect.w = melt_pixels;
1197 src_rect.h = height - ypos[i];
1199 dst_rect.x = dst_x + i * melt_pixels;
1200 dst_rect.y = dst_y + ypos[i];
1202 if (steps_done >= steps_final)
1203 SDL_BlitSurface(surface_source, &src_rect,
1204 surface_screen, &dst_rect);
1210 src_rect.x = src_x + i * melt_pixels;
1212 src_rect.w = melt_pixels;
1213 src_rect.h = height;
1215 dst_rect.x = dst_x + i * melt_pixels;
1218 if (steps_done >= steps_final)
1219 SDL_BlitSurface(surface_target, &src_rect,
1220 surface_screen, &dst_rect);
1224 if (steps_done >= steps_final)
1226 if (draw_border_function != NULL)
1227 draw_border_function();
1229 UpdateScreen_WithFrameDelay(&dst_rect2);
1231 if (PendingEscapeKeyEvent())
1236 else if (fade_mode == FADE_MODE_CURTAIN)
1240 int xx_size = width / 2;
1242 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1244 SDLSetAlpha(surface_source, FALSE, 0); // disable alpha blending
1246 for (xx = 0; xx < xx_size;)
1248 time_last = time_current;
1249 time_current = SDL_GetTicks();
1250 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1251 xx_final = MIN(MAX(0, xx), xx_size);
1256 src_rect.h = height;
1261 // draw new (target) image to screen buffer
1262 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1264 if (xx_final < xx_size)
1266 src_rect.w = xx_size - xx_final;
1267 src_rect.h = height;
1269 // draw old (source) image to screen buffer (left side)
1271 src_rect.x = src_x + xx_final;
1274 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1276 // draw old (source) image to screen buffer (right side)
1278 src_rect.x = src_x + xx_size;
1279 dst_rect.x = dst_x + xx_size + xx_final;
1281 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1284 if (draw_border_function != NULL)
1285 draw_border_function();
1287 // only update the region of the screen that is affected from fading
1288 UpdateScreen_WithFrameDelay(&dst_rect2);
1290 if (PendingEscapeKeyEvent())
1294 else // fading in, fading out or cross-fading
1299 for (alpha = 0.0; alpha < 255.0;)
1301 time_last = time_current;
1302 time_current = SDL_GetTicks();
1303 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1304 alpha_final = MIN(MAX(0, alpha), 255);
1306 // draw existing (source) image to screen buffer
1307 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1309 // draw new (target) image to screen buffer using alpha blending
1310 SDLSetAlpha(surface_target, TRUE, alpha_final);
1311 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1313 if (draw_border_function != NULL)
1314 draw_border_function();
1316 // only update the region of the screen that is affected from fading
1317 UpdateScreen_WithFrameDelay(&dst_rect);
1319 if (PendingEscapeKeyEvent())
1325 Delay_WithScreenUpdates(post_delay);
1327 // restore function for drawing global masked border
1328 gfx.draw_global_border_function = draw_global_border_function;
1330 // after fading in, restore backbuffer (without animation graphics)
1331 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1332 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1335 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1336 int to_x, int to_y, Uint32 color)
1338 SDL_Surface *surface = dst_bitmap->surface;
1342 swap_numbers(&from_x, &to_x);
1345 swap_numbers(&from_y, &to_y);
1349 rect.w = (to_x - from_x + 1);
1350 rect.h = (to_y - from_y + 1);
1352 SDL_FillRect(surface, &rect, color);
1355 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1356 int to_x, int to_y, Uint32 color)
1358 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1361 #if ENABLE_UNUSED_CODE
1362 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1363 int num_points, Uint32 color)
1368 for (i = 0; i < num_points - 1; i++)
1370 for (x = 0; x < line_width; x++)
1372 for (y = 0; y < line_width; y++)
1374 int dx = x - line_width / 2;
1375 int dy = y - line_width / 2;
1377 if ((x == 0 && y == 0) ||
1378 (x == 0 && y == line_width - 1) ||
1379 (x == line_width - 1 && y == 0) ||
1380 (x == line_width - 1 && y == line_width - 1))
1383 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1384 points[i+1].x + dx, points[i+1].y + dy, color);
1391 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1393 SDL_Surface *surface = src_bitmap->surface;
1395 switch (surface->format->BytesPerPixel)
1397 case 1: // assuming 8-bpp
1399 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1403 case 2: // probably 15-bpp or 16-bpp
1405 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1409 case 3: // slow 24-bpp mode; usually not used
1412 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1416 shift = surface->format->Rshift;
1417 color |= *(pix + shift / 8) >> shift;
1418 shift = surface->format->Gshift;
1419 color |= *(pix + shift / 8) >> shift;
1420 shift = surface->format->Bshift;
1421 color |= *(pix + shift / 8) >> shift;
1427 case 4: // probably 32-bpp
1429 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1438 // ============================================================================
1439 // The following functions were taken from the SGE library
1440 // (SDL Graphics Extension Library) by Anders Lindström
1441 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1442 // ============================================================================
1444 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1446 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1448 switch (surface->format->BytesPerPixel)
1453 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1459 // Probably 15-bpp or 16-bpp
1460 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1466 // Slow 24-bpp mode, usually not used
1470 // Gack - slow, but endian correct
1471 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1472 shift = surface->format->Rshift;
1473 *(pix+shift/8) = color>>shift;
1474 shift = surface->format->Gshift;
1475 *(pix+shift/8) = color>>shift;
1476 shift = surface->format->Bshift;
1477 *(pix+shift/8) = color>>shift;
1484 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1492 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1493 Uint8 R, Uint8 G, Uint8 B)
1495 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1498 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1500 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1503 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1505 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1508 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1513 // Gack - slow, but endian correct
1514 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1515 shift = surface->format->Rshift;
1516 *(pix+shift/8) = color>>shift;
1517 shift = surface->format->Gshift;
1518 *(pix+shift/8) = color>>shift;
1519 shift = surface->format->Bshift;
1520 *(pix+shift/8) = color>>shift;
1523 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1525 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1528 static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1530 switch (dest->format->BytesPerPixel)
1533 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1537 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1541 _PutPixel24(dest,x,y,color);
1545 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1551 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1553 if (SDL_MUSTLOCK(surface))
1555 if (SDL_LockSurface(surface) < 0)
1561 _PutPixel(surface, x, y, color);
1563 if (SDL_MUSTLOCK(surface))
1565 SDL_UnlockSurface(surface);
1570 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1571 Uint8 r, Uint8 g, Uint8 b)
1573 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1576 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1578 if (y >= 0 && y <= dest->h - 1)
1580 switch (dest->format->BytesPerPixel)
1583 return y*dest->pitch;
1587 return y*dest->pitch/2;
1591 return y*dest->pitch;
1595 return y*dest->pitch/4;
1603 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1606 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1608 switch (surface->format->BytesPerPixel)
1613 *((Uint8 *)surface->pixels + ypitch + x) = color;
1619 // Probably 15-bpp or 16-bpp
1620 *((Uint16 *)surface->pixels + ypitch + x) = color;
1626 // Slow 24-bpp mode, usually not used
1630 // Gack - slow, but endian correct
1631 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1632 shift = surface->format->Rshift;
1633 *(pix+shift/8) = color>>shift;
1634 shift = surface->format->Gshift;
1635 *(pix+shift/8) = color>>shift;
1636 shift = surface->format->Bshift;
1637 *(pix+shift/8) = color>>shift;
1644 *((Uint32 *)surface->pixels + ypitch + x) = color;
1651 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1656 if (SDL_MUSTLOCK(Surface))
1658 if (SDL_LockSurface(Surface) < 0)
1672 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1676 if (x2 > Surface->w - 1)
1677 x2 = Surface->w - 1;
1684 SDL_FillRect(Surface, &l, Color);
1686 if (SDL_MUSTLOCK(Surface))
1688 SDL_UnlockSurface(Surface);
1692 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1693 Uint8 R, Uint8 G, Uint8 B)
1695 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1698 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1711 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1715 if (x2 > Surface->w - 1)
1716 x2 = Surface->w - 1;
1723 SDL_FillRect(Surface, &l, Color);
1726 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1731 if (SDL_MUSTLOCK(Surface))
1733 if (SDL_LockSurface(Surface) < 0)
1747 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1751 if (y2 > Surface->h - 1)
1752 y2 = Surface->h - 1;
1759 SDL_FillRect(Surface, &l, Color);
1761 if (SDL_MUSTLOCK(Surface))
1763 SDL_UnlockSurface(Surface);
1767 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1768 Uint8 R, Uint8 G, Uint8 B)
1770 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1773 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1786 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1790 if (y2 > Surface->h - 1)
1791 y2 = Surface->h - 1;
1798 SDL_FillRect(Surface, &l, Color);
1802 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1803 Sint16 x2, Sint16 y2, Uint32 Color,
1804 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1807 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1812 sdx = (dx < 0) ? -1 : 1;
1813 sdy = (dy < 0) ? -1 : 1;
1825 for (x = 0; x < dx; x++)
1827 Callback(Surface, px, py, Color);
1841 for (y = 0; y < dy; y++)
1843 Callback(Surface, px, py, Color);
1858 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1859 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1860 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1863 sge_DoLine(Surface, X1, Y1, X2, Y2,
1864 SDL_MapRGB(Surface->format, R, G, B), Callback);
1868 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1871 if (SDL_MUSTLOCK(Surface))
1873 if (SDL_LockSurface(Surface) < 0)
1878 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1880 // unlock the display
1881 if (SDL_MUSTLOCK(Surface))
1883 SDL_UnlockSurface(Surface);
1888 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1889 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1891 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1895 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1897 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1901 // ----------------------------------------------------------------------------
1902 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1903 // ----------------------------------------------------------------------------
1905 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1906 int width, int height, Uint32 color)
1910 for (y = src_y; y < src_y + height; y++)
1912 for (x = src_x; x < src_x + width; x++)
1914 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1916 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1921 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1922 int src_x, int src_y, int width, int height,
1923 int dst_x, int dst_y)
1927 for (y = 0; y < height; y++)
1929 for (x = 0; x < width; x++)
1931 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1933 if (pixel != BLACK_PIXEL)
1934 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1940 // ============================================================================
1941 // The following functions were taken from the SDL_gfx library version 2.0.3
1942 // (Rotozoomer) by Andreas Schiffler
1943 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
1944 // ============================================================================
1946 // ----------------------------------------------------------------------------
1949 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1950 // ----------------------------------------------------------------------------
1960 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1963 tColorRGBA *sp, *csp, *dp;
1967 sp = csp = (tColorRGBA *) src->pixels;
1968 dp = (tColorRGBA *) dst->pixels;
1969 dgap = dst->pitch - dst->w * 4;
1971 for (y = 0; y < dst->h; y++)
1975 for (x = 0; x < dst->w; x++)
1977 tColorRGBA *sp0 = sp;
1978 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1979 tColorRGBA *sp00 = &sp0[0];
1980 tColorRGBA *sp01 = &sp0[1];
1981 tColorRGBA *sp10 = &sp1[0];
1982 tColorRGBA *sp11 = &sp1[1];
1985 // create new color pixel from all four source color pixels
1986 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1987 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1988 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1989 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1994 // advance source pointers
1997 // advance destination pointer
2001 // advance source pointer
2002 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2004 // advance destination pointers
2005 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2011 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2013 int x, y, *sax, *say, *csax, *csay;
2015 tColorRGBA *sp, *csp, *csp0, *dp;
2018 // use specialized zoom function when scaling down to exactly half size
2019 if (src->w == 2 * dst->w &&
2020 src->h == 2 * dst->h)
2021 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2024 sx = (float) src->w / (float) dst->w;
2025 sy = (float) src->h / (float) dst->h;
2027 // allocate memory for row increments
2028 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2029 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2031 // precalculate row increments
2032 for (x = 0; x <= dst->w; x++)
2033 *csax++ = (int)(sx * x);
2035 for (y = 0; y <= dst->h; y++)
2036 *csay++ = (int)(sy * y);
2039 sp = csp = csp0 = (tColorRGBA *) src->pixels;
2040 dp = (tColorRGBA *) dst->pixels;
2041 dgap = dst->pitch - dst->w * 4;
2044 for (y = 0; y < dst->h; y++)
2049 for (x = 0; x < dst->w; x++)
2054 // advance source pointers
2058 // advance destination pointer
2062 // advance source pointer
2064 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2066 // advance destination pointers
2067 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2076 // ----------------------------------------------------------------------------
2079 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2080 // ----------------------------------------------------------------------------
2082 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2084 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2085 Uint8 *sp, *dp, *csp;
2089 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2090 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2092 // allocate memory for row increments
2093 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2094 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2096 // precalculate row increments
2099 for (x = 0; x < dst->w; x++)
2102 *csax = (csx >> 16);
2109 for (y = 0; y < dst->h; y++)
2112 *csay = (csy >> 16);
2119 for (x = 0; x < dst->w; x++)
2127 for (y = 0; y < dst->h; y++)
2134 sp = csp = (Uint8 *) src->pixels;
2135 dp = (Uint8 *) dst->pixels;
2136 dgap = dst->pitch - dst->w;
2140 for (y = 0; y < dst->h; y++)
2144 for (x = 0; x < dst->w; x++)
2149 // advance source pointers
2153 // advance destination pointer
2157 // advance source pointer (for row)
2158 csp += ((*csay) * src->pitch);
2161 // advance destination pointers
2171 // ----------------------------------------------------------------------------
2174 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2175 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2176 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2177 // into a 32bit RGBA format on the fly.
2178 // ----------------------------------------------------------------------------
2180 static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2182 SDL_Surface *zoom_src = NULL;
2183 SDL_Surface *zoom_dst = NULL;
2184 boolean is_converted = FALSE;
2191 // determine if source surface is 32 bit or 8 bit
2192 is_32bit = (src->format->BitsPerPixel == 32);
2194 if (is_32bit || src->format->BitsPerPixel == 8)
2196 // use source surface 'as is'
2201 // new source surface is 32 bit with a defined RGB ordering
2202 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2203 0x000000ff, 0x0000ff00, 0x00ff0000,
2204 (src->format->Amask ? 0xff000000 : 0));
2205 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2207 is_converted = TRUE;
2210 // allocate surface to completely contain the zoomed surface
2213 // target surface is 32 bit with source RGBA/ABGR ordering
2214 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2215 zoom_src->format->Rmask,
2216 zoom_src->format->Gmask,
2217 zoom_src->format->Bmask,
2218 zoom_src->format->Amask);
2222 // target surface is 8 bit
2223 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2227 // lock source surface
2228 SDL_LockSurface(zoom_src);
2230 // check which kind of surface we have
2233 // call the 32 bit transformation routine to do the zooming
2234 zoomSurfaceRGBA(zoom_src, zoom_dst);
2239 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2240 zoom_dst->format->palette->colors[i] =
2241 zoom_src->format->palette->colors[i];
2242 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2244 // call the 8 bit transformation routine to do the zooming
2245 zoomSurfaceY(zoom_src, zoom_dst);
2248 // unlock source surface
2249 SDL_UnlockSurface(zoom_src);
2251 // free temporary surface
2253 SDL_FreeSurface(zoom_src);
2255 // return destination surface
2259 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2261 SDL_Surface *new_surface;
2263 if (surface == NULL)
2266 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2267 Fail("SDLGetNativeSurface() failed");
2269 // remove alpha channel from native non-transparent surface, if defined
2270 SDLSetAlpha(new_surface, FALSE, 0);
2272 // remove transparent color from native non-transparent surface, if defined
2273 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2278 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2280 Bitmap *dst_bitmap = CreateBitmapStruct();
2281 SDL_Surface *src_surface = src_bitmap->surface_masked;
2282 SDL_Surface *dst_surface;
2284 dst_width = MAX(1, dst_width); // prevent zero bitmap width
2285 dst_height = MAX(1, dst_height); // prevent zero bitmap height
2287 dst_bitmap->width = dst_width;
2288 dst_bitmap->height = dst_height;
2290 // create zoomed temporary surface from source surface
2291 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2293 // create native format destination surface from zoomed temporary surface
2294 SDLSetNativeSurface(&dst_surface);
2296 // set color key for zoomed surface from source surface, if defined
2297 if (SDLHasColorKey(src_surface))
2298 SDLCopyColorKey(src_surface, dst_surface);
2300 // create native non-transparent surface for opaque blitting
2301 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2303 // set native transparent surface for masked blitting
2304 dst_bitmap->surface_masked = dst_surface;
2310 // ============================================================================
2311 // load image to bitmap
2312 // ============================================================================
2314 Bitmap *SDLLoadImage(char *filename)
2316 Bitmap *new_bitmap = CreateBitmapStruct();
2317 SDL_Surface *sdl_image_tmp;
2319 if (program.headless)
2321 // prevent sanity check warnings at later stage
2322 new_bitmap->width = new_bitmap->height = 1;
2327 print_timestamp_init("SDLLoadImage");
2329 print_timestamp_time(getBaseNamePtr(filename));
2331 // load image to temporary surface
2332 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2333 Fail("IMG_Load('%s') failed: %s", getBaseNamePtr(filename), SDL_GetError());
2335 print_timestamp_time("IMG_Load");
2337 UPDATE_BUSY_STATE();
2339 // create native non-transparent surface for current image
2340 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2341 Fail("SDLGetOpaqueSurface() failed");
2343 print_timestamp_time("SDLGetNativeSurface (opaque)");
2345 UPDATE_BUSY_STATE();
2347 // set black pixel to transparent if no alpha channel / transparent color
2348 if (!SDLHasAlpha(sdl_image_tmp) &&
2349 !SDLHasColorKey(sdl_image_tmp))
2350 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2351 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2353 // create native transparent surface for current image
2354 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2355 Fail("SDLGetNativeSurface() failed");
2357 print_timestamp_time("SDLGetNativeSurface (masked)");
2359 UPDATE_BUSY_STATE();
2361 // free temporary surface
2362 SDL_FreeSurface(sdl_image_tmp);
2364 new_bitmap->width = new_bitmap->surface->w;
2365 new_bitmap->height = new_bitmap->surface->h;
2367 print_timestamp_done("SDLLoadImage");
2373 // ----------------------------------------------------------------------------
2374 // custom cursor fuctions
2375 // ----------------------------------------------------------------------------
2377 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2379 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2380 cursor_info->width, cursor_info->height,
2381 cursor_info->hot_x, cursor_info->hot_y);
2384 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2386 static struct MouseCursorInfo *last_cursor_info = NULL;
2387 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2388 static SDL_Cursor *cursor_default = NULL;
2389 static SDL_Cursor *cursor_current = NULL;
2391 // if invoked for the first time, store the SDL default cursor
2392 if (cursor_default == NULL)
2393 cursor_default = SDL_GetCursor();
2395 // only create new cursor if cursor info (custom only) has changed
2396 if (cursor_info != NULL && cursor_info != last_cursor_info)
2398 cursor_current = create_cursor(cursor_info);
2399 last_cursor_info = cursor_info;
2402 // only set new cursor if cursor info (custom or NULL) has changed
2403 if (cursor_info != last_cursor_info2)
2404 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2406 last_cursor_info2 = cursor_info;
2410 // ============================================================================
2412 // ============================================================================
2414 void SDLOpenAudio(void)
2416 if (program.headless)
2419 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2421 Warn("SDL_InitSubSystem() failed: %s", SDL_GetError());
2426 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2427 AUDIO_NUM_CHANNELS_STEREO,
2428 setup.system.audio_fragment_size) < 0)
2430 Warn("Mix_OpenAudio() failed: %s", SDL_GetError());
2435 audio.sound_available = TRUE;
2436 audio.music_available = TRUE;
2437 audio.loops_available = TRUE;
2438 audio.sound_enabled = TRUE;
2440 // set number of available mixer channels
2441 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2442 audio.music_channel = MUSIC_CHANNEL;
2443 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2445 Mixer_InitChannels();
2448 void SDLCloseAudio(void)
2451 Mix_HaltChannel(-1);
2454 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2458 // ============================================================================
2460 // ============================================================================
2462 void SDLWaitEvent(Event *event)
2464 SDL_WaitEvent(event);
2467 void SDLCorrectRawMousePosition(int *x, int *y)
2469 if (sdl_renderer == NULL)
2472 // this corrects the raw mouse position for logical screen size within event
2473 // filters (correction done later by SDL library when handling mouse events)
2476 float scale_x, scale_y;
2478 SDL_RenderGetViewport(sdl_renderer, &viewport);
2479 SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2481 *x = (int)(*x / scale_x);
2482 *y = (int)(*y / scale_y);
2489 // ============================================================================
2490 // joystick functions
2491 // ============================================================================
2493 static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick
2494 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2495 static int sdl_js_axis[MAX_PLAYERS][2];
2496 static int sdl_js_button[MAX_PLAYERS][2];
2497 static boolean sdl_is_controller[MAX_PLAYERS];
2499 void SDLClearJoystickState(void)
2503 for (i = 0; i < MAX_PLAYERS; i++)
2505 for (j = 0; j < 2; j++)
2507 sdl_js_axis_raw[i][j] = -1;
2508 sdl_js_axis[i][j] = 0;
2509 sdl_js_button[i][j] = 0;
2514 boolean SDLOpenJoystick(int nr)
2516 if (nr < 0 || nr >= MAX_PLAYERS)
2519 sdl_is_controller[nr] = SDL_IsGameController(nr);
2522 Debug("joystick", "opening joystick %d (%s)",
2523 nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2526 if (sdl_is_controller[nr])
2527 sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2529 sdl_joystick[nr] = SDL_JoystickOpen(nr);
2531 return (sdl_joystick[nr] != NULL);
2534 void SDLCloseJoystick(int nr)
2536 if (nr < 0 || nr >= MAX_PLAYERS)
2540 Debug("joystick", "closing joystick %d", nr);
2543 if (sdl_is_controller[nr])
2544 SDL_GameControllerClose(sdl_joystick[nr]);
2546 SDL_JoystickClose(sdl_joystick[nr]);
2548 sdl_joystick[nr] = NULL;
2551 boolean SDLCheckJoystickOpened(int nr)
2553 if (nr < 0 || nr >= MAX_PLAYERS)
2556 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2559 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2561 int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2562 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2563 axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2564 axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2566 if (nr < 0 || nr >= MAX_PLAYERS)
2572 // prevent (slightly jittering, but centered) axis A from resetting axis B
2573 if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2574 axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2577 sdl_js_axis[nr][axis_id] = axis_value;
2578 sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2581 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2583 int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2584 button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2585 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2586 button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2587 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2588 button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2589 button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2590 button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2593 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2594 sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2595 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2596 sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2597 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2598 sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2599 else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2600 sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2602 if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2603 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2604 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2605 button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2606 sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2608 if (nr < 0 || nr >= MAX_PLAYERS)
2611 if (button_id == -1)
2614 sdl_js_button[nr][button_id] = button_state;
2617 void HandleJoystickEvent(Event *event)
2619 // when using joystick, disable overlay touch buttons
2620 runtime.uses_touch_device = FALSE;
2622 switch (event->type)
2624 case SDL_CONTROLLERDEVICEADDED:
2626 Debug("joystick", "SDL_CONTROLLERDEVICEADDED: device %d added",
2627 event->cdevice.which);
2632 case SDL_CONTROLLERDEVICEREMOVED:
2634 Debug("joystick", "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2635 event->cdevice.which);
2640 case SDL_CONTROLLERAXISMOTION:
2642 Debug("joystick", "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2643 event->caxis.which, event->caxis.axis, event->caxis.value);
2645 setJoystickAxis(event->caxis.which,
2647 event->caxis.value);
2650 case SDL_CONTROLLERBUTTONDOWN:
2652 Debug("joystick", "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2653 event->cbutton.which, event->cbutton.button);
2655 setJoystickButton(event->cbutton.which,
2656 event->cbutton.button,
2660 case SDL_CONTROLLERBUTTONUP:
2662 Debug("joystick", "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2663 event->cbutton.which, event->cbutton.button);
2665 setJoystickButton(event->cbutton.which,
2666 event->cbutton.button,
2670 case SDL_JOYAXISMOTION:
2671 if (sdl_is_controller[event->jaxis.which])
2675 Debug("joystick", "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2676 event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2678 if (event->jaxis.axis < 4)
2679 setJoystickAxis(event->jaxis.which,
2681 event->jaxis.value);
2684 case SDL_JOYBUTTONDOWN:
2685 if (sdl_is_controller[event->jaxis.which])
2689 Debug("joystick", "SDL_JOYBUTTONDOWN: device %d, button %d",
2690 event->jbutton.which, event->jbutton.button);
2692 if (event->jbutton.button < 4)
2693 setJoystickButton(event->jbutton.which,
2694 event->jbutton.button,
2698 case SDL_JOYBUTTONUP:
2699 if (sdl_is_controller[event->jaxis.which])
2703 Debug("joystick", "SDL_JOYBUTTONUP: device %d, button %d",
2704 event->jbutton.which, event->jbutton.button);
2706 if (event->jbutton.button < 4)
2707 setJoystickButton(event->jbutton.which,
2708 event->jbutton.button,
2717 void SDLInitJoysticks(void)
2719 static boolean sdl_joystick_subsystem_initialized = FALSE;
2720 boolean print_warning = !sdl_joystick_subsystem_initialized;
2721 char *mappings_file_base = getPath2(options.conf_directory,
2722 GAMECONTROLLER_BASENAME);
2723 char *mappings_file_user = getPath2(getMainUserGameDataDir(),
2724 GAMECONTROLLER_BASENAME);
2728 if (!sdl_joystick_subsystem_initialized)
2730 sdl_joystick_subsystem_initialized = TRUE;
2732 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2734 if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2735 Fail("SDL_Init() failed: %s", SDL_GetError());
2737 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2739 // the included game controller base mappings should always be found
2740 if (num_mappings == -1)
2741 Warn("no game controller base mappings found");
2744 Debug("joystick", "%d game controller base mapping(s) added",
2748 num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2751 // the personal game controller user mappings may or may not be found
2752 if (num_mappings == -1)
2753 Warn("no game controller user mappings found");
2755 Debug("joystick", , "%d game controller user mapping(s) added",
2758 Debug("joystick", "%d joystick(s) found:", SDL_NumJoysticks());
2761 checked_free(mappings_file_base);
2762 checked_free(mappings_file_user);
2765 for (i = 0; i < SDL_NumJoysticks(); i++)
2767 const char *name, *type;
2769 if (SDL_IsGameController(i))
2771 name = SDL_GameControllerNameForIndex(i);
2772 type = "game controller";
2776 name = SDL_JoystickNameForIndex(i);
2780 Debug("joystick", "- joystick %d (%s): '%s'",
2781 i, type, (name ? name : "(Unknown)"));
2786 // assign joysticks from configured to connected joystick for all players
2787 for (i = 0; i < MAX_PLAYERS; i++)
2789 // get configured joystick for this player
2790 char *device_name = setup.input[i].joy.device_name;
2791 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2793 if (joystick_nr >= SDL_NumJoysticks())
2795 if (setup.input[i].use_joystick && print_warning)
2796 Warn("cannot find joystick %d", joystick_nr);
2801 // store configured joystick number for each player
2802 joystick.nr[i] = joystick_nr;
2805 // now open all connected joysticks (regardless if configured or not)
2806 for (i = 0; i < SDL_NumJoysticks(); i++)
2808 // this allows subsequent calls to 'InitJoysticks' for re-initialization
2809 if (SDLCheckJoystickOpened(i))
2810 SDLCloseJoystick(i);
2812 if (SDLOpenJoystick(i))
2813 joystick.status = JOYSTICK_ACTIVATED;
2814 else if (print_warning)
2815 Warn("cannot open joystick %d", i);
2818 SDLClearJoystickState();
2821 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2823 if (nr < 0 || nr >= MAX_PLAYERS)
2827 *x = sdl_js_axis[nr][0];
2829 *y = sdl_js_axis[nr][1];
2832 *b1 = sdl_js_button[nr][0];
2834 *b2 = sdl_js_button[nr][1];
2840 // ============================================================================
2841 // touch input overlay functions
2842 // ============================================================================
2844 #if defined(USE_TOUCH_INPUT_OVERLAY)
2845 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2848 int grid_xsize = overlay.grid_xsize;
2849 int grid_ysize = overlay.grid_ysize;
2852 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2853 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2855 for (x = 0; x < grid_xsize; x++)
2857 rect.x = (x + 0) * video.screen_width / grid_xsize;
2858 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2860 for (y = 0; y < grid_ysize; y++)
2862 rect.y = (y + 0) * video.screen_height / grid_ysize;
2863 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2865 if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2866 SDL_RenderDrawRect(sdl_renderer, &rect);
2870 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2873 static void RenderFillRectangle(int x, int y, int width, int height)
2875 SDL_Rect rect = { x, y, width, height };
2877 SDL_RenderFillRect(sdl_renderer, &rect);
2880 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2882 static int alpha_direction = 0;
2883 static int alpha_highlight = 0;
2884 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2885 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2887 int grid_xsize = overlay.grid_xsize;
2888 int grid_ysize = overlay.grid_ysize;
2891 if (alpha == alpha_max)
2893 if (alpha_direction < 0)
2895 alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2897 if (alpha_highlight == 0)
2898 alpha_direction = 1;
2902 alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2904 if (alpha_highlight == alpha_max)
2905 alpha_direction = -1;
2910 alpha_direction = 1;
2911 alpha_highlight = alpha;
2914 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2916 for (x = 0; x < grid_xsize; x++)
2918 for (y = 0; y < grid_ysize; y++)
2920 int grid_button = overlay.grid_button[x][y];
2921 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2922 int alpha_draw = alpha;
2923 int outline_border = MV_NONE;
2924 int border_size = 2;
2925 boolean draw_outlined = setup.touch.draw_outlined;
2926 boolean draw_pressed = setup.touch.draw_pressed;
2928 if (grid_button == CHAR_GRID_BUTTON_NONE)
2931 if (grid_button == overlay.grid_button_highlight)
2933 draw_outlined = FALSE;
2934 alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2937 if (draw_pressed && overlay.grid_button_action & grid_button_action)
2940 draw_outlined = FALSE;
2942 alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
2945 SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
2947 rect.x = (x + 0) * video.screen_width / grid_xsize;
2948 rect.y = (y + 0) * video.screen_height / grid_ysize;
2949 rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2950 rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2952 if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
2954 rect.x += border_size;
2955 rect.w -= border_size;
2957 outline_border |= MV_LEFT;
2960 if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
2962 rect.w -= border_size;
2964 outline_border |= MV_RIGHT;
2967 if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
2969 rect.y += border_size;
2970 rect.h -= border_size;
2972 outline_border |= MV_UP;
2975 if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
2977 rect.h -= border_size;
2979 outline_border |= MV_DOWN;
2984 int rect_x = rect.x +
2985 (outline_border & MV_LEFT ? border_size : 0);
2986 int rect_w = rect.w -
2987 (outline_border & MV_LEFT ? border_size : 0) -
2988 (outline_border & MV_RIGHT ? border_size : 0);
2990 if (outline_border & MV_LEFT)
2991 RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
2993 if (outline_border & MV_RIGHT)
2994 RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
2995 border_size, rect.h);
2997 if (outline_border & MV_UP)
2998 RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
3000 if (outline_border & MV_DOWN)
3001 RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
3002 rect_w, border_size);
3006 SDL_RenderFillRect(sdl_renderer, &rect);
3011 SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3014 static void DrawTouchInputOverlay(void)
3016 static boolean deactivated = TRUE;
3017 static boolean show_grid = FALSE;
3018 static int alpha = 0;
3019 int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3020 int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3021 boolean active = (overlay.enabled && overlay.active);
3023 if (!active && deactivated)
3028 if (alpha < alpha_max)
3029 alpha = MIN(alpha + alpha_step, alpha_max);
3031 deactivated = FALSE;
3035 alpha = MAX(0, alpha - alpha_step);
3041 if (overlay.show_grid)
3043 else if (deactivated)
3047 DrawTouchInputOverlay_ShowGrid(alpha);
3049 DrawTouchInputOverlay_ShowGridButtons(alpha);
3052 static void DrawTouchGadgetsOverlay(void)
3054 DrawGadgets_OverlayTouchButtons();