1 //
2 // Copyright(C) 1993-1996 Id Software, Inc.
3 // Copyright(C) 2005-2014 Simon Howard
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // DESCRIPTION:
16 //	DOOM graphics stuff for SDL.
17 //
18 
19 
20 #include <stdlib.h>
21 
22 #include "SDL.h"
23 #include "SDL_opengl.h"
24 
25 #ifdef _WIN32
26 #ifndef WIN32_LEAN_AND_MEAN
27 #define WIN32_LEAN_AND_MEAN
28 #endif
29 #include <windows.h>
30 #endif
31 
32 #include "icon.c"
33 
34 #include "crispy.h"
35 
36 #include "config.h"
37 #include "d_loop.h"
38 #include "deh_str.h"
39 #include "doomtype.h"
40 #include "i_input.h"
41 #include "i_joystick.h"
42 #include "i_system.h"
43 #include "i_timer.h"
44 #include "i_video.h"
45 #include "m_argv.h"
46 #include "m_config.h"
47 #include "m_misc.h"
48 #include "tables.h"
49 #include "v_diskicon.h"
50 #include "v_video.h"
51 #include "w_wad.h"
52 #include "z_zone.h"
53 
54 int SCREENWIDTH, SCREENHEIGHT, SCREENHEIGHT_4_3;
55 int NONWIDEWIDTH; // [crispy] non-widescreen SCREENWIDTH
56 int WIDESCREENDELTA; // [crispy] horizontal widescreen offset
57 
58 // These are (1) the window (or the full screen) that our game is rendered to
59 // and (2) the renderer that scales the texture (see below) into this window.
60 
61 static SDL_Window *screen;
62 static SDL_Renderer *renderer;
63 
64 // Window title
65 
66 static const char *window_title = "";
67 
68 // These are (1) the 320x200x8 paletted buffer that we draw to (i.e. the one
69 // that holds I_VideoBuffer), (2) the 320x200x32 RGBA intermediate buffer that
70 // we blit the former buffer to, (3) the intermediate 320x200 texture that we
71 // load the RGBA buffer to and that we render into another texture (4) which
72 // is upscaled by an integer factor UPSCALE using "nearest" scaling and which
73 // in turn is finally rendered to screen using "linear" scaling.
74 
75 #ifndef CRISPY_TRUECOLOR
76 static SDL_Surface *screenbuffer = NULL;
77 #endif
78 static SDL_Surface *argbbuffer = NULL;
79 static SDL_Texture *texture = NULL;
80 static SDL_Texture *texture_upscaled = NULL;
81 
82 #ifndef CRISPY_TRUECOLOR
83 static SDL_Rect blit_rect = {
84     0,
85     0,
86     MAXWIDTH,
87     MAXHEIGHT
88 };
89 #endif
90 
91 static uint32_t pixel_format;
92 
93 // palette
94 
95 #ifdef CRISPY_TRUECOLOR
96 static SDL_Texture *curpane = NULL;
97 static SDL_Texture *redpane = NULL;
98 static SDL_Texture *yelpane = NULL;
99 static SDL_Texture *grnpane = NULL;
100 static int pane_alpha;
101 static unsigned int rmask, gmask, bmask, amask; // [crispy] moved up here
102 static const uint8_t blend_alpha = 0xa8;
103 extern pixel_t* colormaps; // [crispy] evil hack to get FPS dots working as in Vanilla
104 #else
105 static SDL_Color palette[256];
106 #endif
107 static boolean palette_to_set;
108 
109 // display has been set up?
110 
111 static boolean initialized = false;
112 
113 // disable mouse?
114 
115 static boolean nomouse = false;
116 int usemouse = 1;
117 
118 // Save screenshots in PNG format.
119 
120 int png_screenshots = 1; // [crispy]
121 
122 // SDL video driver name
123 
124 char *video_driver = "";
125 
126 // Window position:
127 
128 char *window_position = "center";
129 
130 // SDL display number on which to run.
131 
132 int video_display = 0;
133 
134 // Screen width and height, from configuration file.
135 
136 int window_width = 800;
137 int window_height = 600;
138 
139 // Fullscreen mode, 0x0 for SDL_WINDOW_FULLSCREEN_DESKTOP.
140 
141 int fullscreen_width = 0, fullscreen_height = 0;
142 
143 // Maximum number of pixels to use for intermediate scale buffer.
144 
145 static int max_scaling_buffer_pixels = 16000000;
146 
147 // Run in full screen mode?  (int type for config code)
148 
149 int fullscreen = true;
150 
151 // Aspect ratio correction mode
152 
153 int aspect_ratio_correct = true;
154 static int actualheight;
155 
156 // Force integer scales for resolution-independent rendering
157 
158 int integer_scaling = false;
159 
160 // VGA Porch palette change emulation
161 
162 int vga_porch_flash = false;
163 
164 // Force software rendering, for systems which lack effective hardware
165 // acceleration
166 
167 int force_software_renderer = false;
168 
169 // Time to wait for the screen to settle on startup before starting the
170 // game (ms)
171 
172 static int startup_delay = 1000;
173 
174 // Grab the mouse? (int type for config code). nograbmouse_override allows
175 // this to be temporarily disabled via the command line.
176 
177 static int grabmouse = true;
178 static boolean nograbmouse_override = false;
179 
180 // The screen buffer; this is modified to draw things to the screen
181 
182 pixel_t *I_VideoBuffer = NULL;
183 
184 // If true, game is running as a screensaver
185 
186 boolean screensaver_mode = false;
187 
188 // Flag indicating whether the screen is currently visible:
189 // when the screen isnt visible, don't render the screen
190 
191 boolean screenvisible = true;
192 
193 // If true, we display dots at the bottom of the screen to
194 // indicate FPS.
195 
196 static boolean display_fps_dots;
197 
198 // If this is true, the screen is rendered but not blitted to the
199 // video buffer.
200 
201 static boolean noblit;
202 
203 // Callback function to invoke to determine whether to grab the
204 // mouse pointer.
205 
206 static grabmouse_callback_t grabmouse_callback = NULL;
207 
208 // Does the window currently have focus?
209 
210 static boolean window_focused = true;
211 
212 // Window resize state.
213 
214 static boolean need_resize = false;
215 static unsigned int last_resize_time;
216 #define RESIZE_DELAY 500
217 
218 // Gamma correction level to use
219 
220 int usegamma = 0;
221 
222 // Joystick/gamepad hysteresis
223 unsigned int joywait = 0;
224 
MouseShouldBeGrabbed()225 static boolean MouseShouldBeGrabbed()
226 {
227     // never grab the mouse when in screensaver mode
228 
229     if (screensaver_mode)
230         return false;
231 
232     // if the window doesn't have focus, never grab it
233 
234     if (!window_focused)
235         return false;
236 
237     // always grab the mouse when full screen (dont want to
238     // see the mouse pointer)
239 
240     if (fullscreen)
241         return true;
242 
243     // Don't grab the mouse if mouse input is disabled
244 
245     if (!usemouse || nomouse)
246         return false;
247 
248     // if we specify not to grab the mouse, never grab
249 
250     if (nograbmouse_override || !grabmouse)
251         return false;
252 
253     // Invoke the grabmouse callback function to determine whether
254     // the mouse should be grabbed
255 
256     if (grabmouse_callback != NULL)
257     {
258         return grabmouse_callback();
259     }
260     else
261     {
262         return true;
263     }
264 }
265 
I_SetGrabMouseCallback(grabmouse_callback_t func)266 void I_SetGrabMouseCallback(grabmouse_callback_t func)
267 {
268     grabmouse_callback = func;
269 }
270 
271 // Set the variable controlling FPS dots.
272 
I_DisplayFPSDots(boolean dots_on)273 void I_DisplayFPSDots(boolean dots_on)
274 {
275     display_fps_dots = dots_on;
276 }
277 
SetShowCursor(boolean show)278 static void SetShowCursor(boolean show)
279 {
280     if (!screensaver_mode)
281     {
282         // When the cursor is hidden, grab the input.
283         // Relative mode implicitly hides the cursor.
284         SDL_SetRelativeMouseMode(!show);
285         SDL_GetRelativeMouseState(NULL, NULL);
286     }
287 }
288 
I_ShutdownGraphics(void)289 void I_ShutdownGraphics(void)
290 {
291     if (initialized)
292     {
293         SetShowCursor(true);
294 
295         SDL_QuitSubSystem(SDL_INIT_VIDEO);
296 
297         initialized = false;
298     }
299 }
300 
301 
302 
303 //
304 // I_StartFrame
305 //
I_StartFrame(void)306 void I_StartFrame (void)
307 {
308     // er?
309 
310 }
311 
312 // Adjust window_width / window_height variables to be an an aspect
313 // ratio consistent with the aspect_ratio_correct variable.
AdjustWindowSize(void)314 static void AdjustWindowSize(void)
315 {
316     if (aspect_ratio_correct || integer_scaling)
317     {
318         if (window_width * actualheight <= window_height * SCREENWIDTH)
319         {
320             // We round up window_height if the ratio is not exact; this leaves
321             // the result stable.
322             window_height = (window_width * actualheight + SCREENWIDTH - 1) / SCREENWIDTH;
323         }
324         else
325         {
326             window_width = window_height * SCREENWIDTH / actualheight;
327         }
328     }
329 }
330 
HandleWindowEvent(SDL_WindowEvent * event)331 static void HandleWindowEvent(SDL_WindowEvent *event)
332 {
333     int i;
334 
335     switch (event->event)
336     {
337 #if 0 // SDL2-TODO
338         case SDL_ACTIVEEVENT:
339             // need to update our focus state
340             UpdateFocus();
341             break;
342 #endif
343         case SDL_WINDOWEVENT_EXPOSED:
344             palette_to_set = true;
345             break;
346 
347         case SDL_WINDOWEVENT_RESIZED:
348             need_resize = true;
349             last_resize_time = SDL_GetTicks();
350             break;
351 
352         // Don't render the screen when the window is minimized:
353 
354         case SDL_WINDOWEVENT_MINIMIZED:
355             screenvisible = false;
356             break;
357 
358         case SDL_WINDOWEVENT_MAXIMIZED:
359         case SDL_WINDOWEVENT_RESTORED:
360             screenvisible = true;
361             break;
362 
363         // Update the value of window_focused when we get a focus event
364         //
365         // We try to make ourselves be well-behaved: the grab on the mouse
366         // is removed if we lose focus (such as a popup window appearing),
367         // and we dont move the mouse around if we aren't focused either.
368 
369         case SDL_WINDOWEVENT_FOCUS_GAINED:
370             window_focused = true;
371             break;
372 
373         case SDL_WINDOWEVENT_FOCUS_LOST:
374             window_focused = false;
375             break;
376 
377         // We want to save the user's preferred monitor to use for running the
378         // game, so that next time we're run we start on the same display. So
379         // every time the window is moved, find which display we're now on and
380         // update the video_display config variable.
381 
382         case SDL_WINDOWEVENT_MOVED:
383             i = SDL_GetWindowDisplayIndex(screen);
384             if (i >= 0)
385             {
386                 video_display = i;
387             }
388             break;
389 
390         default:
391             break;
392     }
393 }
394 
ToggleFullScreenKeyShortcut(SDL_Keysym * sym)395 static boolean ToggleFullScreenKeyShortcut(SDL_Keysym *sym)
396 {
397     Uint16 flags = (KMOD_LALT | KMOD_RALT);
398 #if defined(__MACOSX__)
399     flags |= (KMOD_LGUI | KMOD_RGUI);
400 #endif
401     return (sym->scancode == SDL_SCANCODE_RETURN ||
402             sym->scancode == SDL_SCANCODE_KP_ENTER) && (sym->mod & flags) != 0;
403 }
404 
I_ToggleFullScreen(void)405 static void I_ToggleFullScreen(void)
406 {
407     unsigned int flags = 0;
408 
409     // TODO: Consider implementing fullscreen toggle for SDL_WINDOW_FULLSCREEN
410     // (mode-changing) setup. This is hard because we have to shut down and
411     // restart again.
412     if (fullscreen_width != 0 || fullscreen_height != 0)
413     {
414         return;
415     }
416 
417     fullscreen = !fullscreen;
418 
419     if (fullscreen)
420     {
421         flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
422     }
423 
424     SDL_SetWindowFullscreen(screen, flags);
425 
426     if (!fullscreen)
427     {
428         AdjustWindowSize();
429         SDL_SetWindowSize(screen, window_width, window_height);
430     }
431 }
432 
I_GetEvent(void)433 void I_GetEvent(void)
434 {
435     extern void I_HandleKeyboardEvent(SDL_Event *sdlevent);
436     extern void I_HandleMouseEvent(SDL_Event *sdlevent);
437     SDL_Event sdlevent;
438 
439     SDL_PumpEvents();
440 
441     while (SDL_PollEvent(&sdlevent))
442     {
443         switch (sdlevent.type)
444         {
445             case SDL_KEYDOWN:
446                 if (ToggleFullScreenKeyShortcut(&sdlevent.key.keysym))
447                 {
448                     I_ToggleFullScreen();
449                     break;
450                 }
451                 // deliberate fall-though
452 
453             case SDL_KEYUP:
454 		I_HandleKeyboardEvent(&sdlevent);
455                 break;
456 
457             case SDL_MOUSEBUTTONDOWN:
458             case SDL_MOUSEBUTTONUP:
459             case SDL_MOUSEWHEEL:
460                 if (usemouse && !nomouse && window_focused)
461                 {
462                     I_HandleMouseEvent(&sdlevent);
463                 }
464                 break;
465 
466             case SDL_QUIT:
467                 if (screensaver_mode)
468                 {
469                     I_Quit();
470                 }
471                 else
472                 {
473                     event_t event;
474                     event.type = ev_quit;
475                     D_PostEvent(&event);
476                 }
477                 break;
478 
479             case SDL_WINDOWEVENT:
480                 if (sdlevent.window.windowID == SDL_GetWindowID(screen))
481                 {
482                     HandleWindowEvent(&sdlevent.window);
483                 }
484                 break;
485 
486             default:
487                 break;
488         }
489     }
490 }
491 
492 //
493 // I_StartTic
494 //
I_StartTic(void)495 void I_StartTic (void)
496 {
497     if (!initialized)
498     {
499         return;
500     }
501 
502     I_GetEvent();
503 
504     if (usemouse && !nomouse && window_focused)
505     {
506         I_ReadMouse();
507     }
508 
509     if (joywait < I_GetTime())
510     {
511         I_UpdateJoystick();
512     }
513 }
514 
515 
516 //
517 // I_UpdateNoBlit
518 //
I_UpdateNoBlit(void)519 void I_UpdateNoBlit (void)
520 {
521     // what is this?
522 }
523 
UpdateGrab(void)524 static void UpdateGrab(void)
525 {
526     static boolean currently_grabbed = false;
527     boolean grab;
528 
529     grab = MouseShouldBeGrabbed();
530 
531     if (screensaver_mode)
532     {
533         // Hide the cursor in screensaver mode
534 
535         SetShowCursor(false);
536     }
537     else if (grab && !currently_grabbed)
538     {
539         SetShowCursor(false);
540     }
541     else if (!grab && currently_grabbed)
542     {
543         int screen_w, screen_h;
544 
545         SetShowCursor(true);
546 
547         // When releasing the mouse from grab, warp the mouse cursor to
548         // the bottom-right of the screen. This is a minimally distracting
549         // place for it to appear - we may only have released the grab
550         // because we're at an end of level intermission screen, for
551         // example.
552 
553         SDL_GetWindowSize(screen, &screen_w, &screen_h);
554         SDL_WarpMouseInWindow(screen, screen_w - 16, screen_h - 16);
555         SDL_GetRelativeMouseState(NULL, NULL);
556     }
557 
558     currently_grabbed = grab;
559 }
560 
LimitTextureSize(int * w_upscale,int * h_upscale)561 static void LimitTextureSize(int *w_upscale, int *h_upscale)
562 {
563     SDL_RendererInfo rinfo;
564     int orig_w, orig_h;
565 
566     orig_w = *w_upscale;
567     orig_h = *h_upscale;
568 
569     // Query renderer and limit to maximum texture dimensions of hardware:
570     if (SDL_GetRendererInfo(renderer, &rinfo) != 0)
571     {
572         I_Error("CreateUpscaledTexture: SDL_GetRendererInfo() call failed: %s",
573                 SDL_GetError());
574     }
575 
576     while (*w_upscale * SCREENWIDTH > rinfo.max_texture_width)
577     {
578         --*w_upscale;
579     }
580     while (*h_upscale * SCREENHEIGHT > rinfo.max_texture_height)
581     {
582         --*h_upscale;
583     }
584 
585     if ((*w_upscale < 1 && rinfo.max_texture_width > 0) ||
586         (*h_upscale < 1 && rinfo.max_texture_height > 0))
587     {
588         I_Error("CreateUpscaledTexture: Can't create a texture big enough for "
589                 "the whole screen! Maximum texture size %dx%d",
590                 rinfo.max_texture_width, rinfo.max_texture_height);
591     }
592 
593     // We limit the amount of texture memory used for the intermediate buffer,
594     // since beyond a certain point there are diminishing returns. Also,
595     // depending on the hardware there may be performance problems with very
596     // huge textures, so the user can use this to reduce the maximum texture
597     // size if desired.
598 
599     if (max_scaling_buffer_pixels < SCREENWIDTH * SCREENHEIGHT)
600     {
601         I_Error("CreateUpscaledTexture: max_scaling_buffer_pixels too small "
602                 "to create a texture buffer: %d < %d",
603                 max_scaling_buffer_pixels, SCREENWIDTH * SCREENHEIGHT);
604     }
605 
606     while (*w_upscale * *h_upscale * SCREENWIDTH * SCREENHEIGHT
607            > max_scaling_buffer_pixels)
608     {
609         if (*w_upscale > *h_upscale)
610         {
611             --*w_upscale;
612         }
613         else
614         {
615             --*h_upscale;
616         }
617     }
618 
619     if (*w_upscale != orig_w || *h_upscale != orig_h)
620     {
621         printf("CreateUpscaledTexture: Limited texture size to %dx%d "
622                "(max %d pixels, max texture size %dx%d)\n",
623                *w_upscale * SCREENWIDTH, *h_upscale * SCREENHEIGHT,
624                max_scaling_buffer_pixels,
625                rinfo.max_texture_width, rinfo.max_texture_height);
626     }
627 }
628 
CreateUpscaledTexture(boolean force)629 static void CreateUpscaledTexture(boolean force)
630 {
631     int w, h;
632     int h_upscale, w_upscale;
633     static int h_upscale_old, w_upscale_old;
634 
635     SDL_Texture *new_texture, *old_texture;
636 
637     // Get the size of the renderer output. The units this gives us will be
638     // real world pixels, which are not necessarily equivalent to the screen's
639     // window size (because of highdpi).
640     if (SDL_GetRendererOutputSize(renderer, &w, &h) != 0)
641     {
642         I_Error("Failed to get renderer output size: %s", SDL_GetError());
643     }
644 
645     // When the screen or window dimensions do not match the aspect ratio
646     // of the texture, the rendered area is scaled down to fit. Calculate
647     // the actual dimensions of the rendered area.
648 
649     if (w * actualheight < h * SCREENWIDTH)
650     {
651         // Tall window.
652 
653         h = w * actualheight / SCREENWIDTH;
654     }
655     else
656     {
657         // Wide window.
658 
659         w = h * SCREENWIDTH / actualheight;
660     }
661 
662     // Pick texture size the next integer multiple of the screen dimensions.
663     // If one screen dimension matches an integer multiple of the original
664     // resolution, there is no need to overscale in this direction.
665 
666     w_upscale = (w + SCREENWIDTH - 1) / SCREENWIDTH;
667     h_upscale = (h + SCREENHEIGHT - 1) / SCREENHEIGHT;
668 
669     // Minimum texture dimensions of 320x200.
670 
671     if (w_upscale < 1)
672     {
673         w_upscale = 1;
674     }
675     if (h_upscale < 1)
676     {
677         h_upscale = 1;
678     }
679 
680     LimitTextureSize(&w_upscale, &h_upscale);
681 
682     // Create a new texture only if the upscale factors have actually changed.
683 
684     if (h_upscale == h_upscale_old && w_upscale == w_upscale_old && !force)
685     {
686         return;
687     }
688 
689     h_upscale_old = h_upscale;
690     w_upscale_old = w_upscale;
691 
692     // Set the scaling quality for rendering the upscaled texture to "linear",
693     // which looks much softer and smoother than "nearest" but does a better
694     // job at downscaling from the upscaled texture to screen.
695 
696     SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
697 
698     new_texture = SDL_CreateTexture(renderer,
699                                 pixel_format,
700                                 SDL_TEXTUREACCESS_TARGET,
701                                 w_upscale*SCREENWIDTH,
702                                 h_upscale*SCREENHEIGHT);
703 
704     old_texture = texture_upscaled;
705     texture_upscaled = new_texture;
706 
707     if (old_texture != NULL)
708     {
709         SDL_DestroyTexture(old_texture);
710     }
711 }
712 
713 // [AM] Fractional part of the current tic, in the half-open
714 //      range of [0.0, 1.0).  Used for interpolation.
715 fixed_t fractionaltic;
716 
717 //
718 // I_FinishUpdate
719 //
I_FinishUpdate(void)720 void I_FinishUpdate (void)
721 {
722     static int lasttic;
723     int tics;
724     int i;
725 
726     if (!initialized)
727         return;
728 
729     if (noblit)
730         return;
731 
732     if (need_resize)
733     {
734         if (SDL_GetTicks() > last_resize_time + RESIZE_DELAY)
735         {
736             int flags;
737             // When the window is resized (we're not in fullscreen mode),
738             // save the new window size.
739             flags = SDL_GetWindowFlags(screen);
740             if ((flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == 0)
741             {
742                 int old_height;
743                 SDL_GetWindowSize(screen, &window_width, &window_height);
744                 old_height = window_height;
745 
746                 // Adjust the window by resizing again so that the window
747                 // is the right aspect ratio.
748                 AdjustWindowSize();
749                 if (window_height < old_height)
750                     window_height = old_height;
751                 SDL_SetWindowSize(screen, window_width, window_height);
752             }
753             CreateUpscaledTexture(false);
754             need_resize = false;
755             palette_to_set = true;
756         }
757         else
758         {
759             return;
760         }
761     }
762 
763     UpdateGrab();
764 
765 #if 0 // SDL2-TODO
766     // Don't update the screen if the window isn't visible.
767     // Not doing this breaks under Windows when we alt-tab away
768     // while fullscreen.
769 
770     if (!(SDL_GetAppState() & SDL_APPACTIVE))
771         return;
772 #endif
773 
774     // draws little dots on the bottom of the screen
775 
776     if (display_fps_dots)
777     {
778 	i = I_GetTime();
779 	tics = i - lasttic;
780 	lasttic = i;
781 	if (tics > 20) tics = 20;
782 
783 	for (i=0 ; i<tics*4 ; i+=4)
784 #ifndef CRISPY_TRUECOLOR
785 	    I_VideoBuffer[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = 0xff;
786 #else
787 	    I_VideoBuffer[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = colormaps[0xff];
788 #endif
789 	for ( ; i<20*4 ; i+=4)
790 #ifndef CRISPY_TRUECOLOR
791 	    I_VideoBuffer[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = 0x0;
792 #else
793 	    I_VideoBuffer[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = colormaps[0x0];
794 #endif
795     }
796 
797 	// [crispy] [AM] Real FPS counter
798 	{
799 		static int lastmili;
800 		static int fpscount;
801 		int mili;
802 
803 		fpscount++;
804 
805 		i = SDL_GetTicks();
806 		mili = i - lastmili;
807 
808 		// Update FPS counter every second
809 		if (mili >= 1000)
810 		{
811 			crispy->fps = (fpscount * 1000) / mili;
812 			fpscount = 0;
813 			lastmili = i;
814 		}
815 	}
816 
817     // Draw disk icon before blit, if necessary.
818     V_DrawDiskIcon();
819 
820 #ifndef CRISPY_TRUECOLOR
821     if (palette_to_set)
822     {
823         SDL_SetPaletteColors(screenbuffer->format->palette, palette, 0, 256);
824         palette_to_set = false;
825 
826         if (vga_porch_flash)
827         {
828             // "flash" the pillars/letterboxes with palette changes, emulating
829             // VGA "porch" behaviour (GitHub issue #832)
830             SDL_SetRenderDrawColor(renderer, palette[0].r, palette[0].g,
831                 palette[0].b, SDL_ALPHA_OPAQUE);
832         }
833     }
834 
835     // Blit from the paletted 8-bit screen buffer to the intermediate
836     // 32-bit RGBA buffer that we can load into the texture.
837 
838     SDL_LowerBlit(screenbuffer, &blit_rect, argbbuffer, &blit_rect);
839 #endif
840 
841     // Update the intermediate texture with the contents of the RGBA buffer.
842 
843     SDL_UpdateTexture(texture, NULL, argbbuffer->pixels, argbbuffer->pitch);
844 
845     // Make sure the pillarboxes are kept clear each frame.
846 
847     SDL_RenderClear(renderer);
848 
849     if (crispy->smoothscaling)
850     {
851     // Render this intermediate texture into the upscaled texture
852     // using "nearest" integer scaling.
853 
854     SDL_SetRenderTarget(renderer, texture_upscaled);
855     SDL_RenderCopy(renderer, texture, NULL, NULL);
856 
857     // Finally, render this upscaled texture to screen using linear scaling.
858 
859     SDL_SetRenderTarget(renderer, NULL);
860     SDL_RenderCopy(renderer, texture_upscaled, NULL, NULL);
861     }
862     else
863     {
864 	SDL_SetRenderTarget(renderer, NULL);
865 	SDL_RenderCopy(renderer, texture, NULL, NULL);
866     }
867 
868 #ifdef CRISPY_TRUECOLOR
869     if (curpane)
870     {
871 	SDL_SetTextureAlphaMod(curpane, pane_alpha);
872 	SDL_RenderCopy(renderer, curpane, NULL, NULL);
873     }
874 #endif
875 
876     // Draw!
877 
878     SDL_RenderPresent(renderer);
879 
880     // [AM] Figure out how far into the current tic we're in as a fixed_t.
881     if (crispy->uncapped)
882     {
883 	fractionaltic = I_GetTimeMS() * TICRATE % 1000 * FRACUNIT / 1000;
884     }
885 
886     // Restore background and undo the disk indicator, if it was drawn.
887     V_RestoreDiskBackground();
888 }
889 
890 
891 //
892 // I_ReadScreen
893 //
I_ReadScreen(pixel_t * scr)894 void I_ReadScreen (pixel_t* scr)
895 {
896     memcpy(scr, I_VideoBuffer, SCREENWIDTH*SCREENHEIGHT*sizeof(*scr));
897 }
898 
899 
900 //
901 // I_SetPalette
902 //
903 // [crispy] intermediate gamma levels
904 byte **gamma2table = NULL;
I_SetGammaTable(void)905 void I_SetGammaTable (void)
906 {
907 	int i;
908 
909 	gamma2table = malloc(9 * sizeof(*gamma2table));
910 
911 	// [crispy] 5 original gamma levels
912 	for (i = 0; i < 5; i++)
913 	{
914 		gamma2table[2*i] = (byte *)gammatable[i];
915 	}
916 
917 	// [crispy] 4 intermediate gamma levels
918 	for (i = 0; i < 4; i++)
919 	{
920 		int j;
921 
922 		gamma2table[2*i+1] = malloc(256 * sizeof(**gamma2table));
923 
924 		for (j = 0; j < 256; j++)
925 		{
926 			gamma2table[2*i+1][j] = (gamma2table[2*i][j] + gamma2table[2*i+2][j]) / 2;
927 		}
928 	}
929 }
930 
931 #ifndef CRISPY_TRUECOLOR
I_SetPalette(byte * doompalette)932 void I_SetPalette (byte *doompalette)
933 {
934     int i;
935 
936     // [crispy] intermediate gamma levels
937     if (!gamma2table)
938     {
939         I_SetGammaTable();
940     }
941 
942     for (i=0; i<256; ++i)
943     {
944         // Zero out the bottom two bits of each channel - the PC VGA
945         // controller only supports 6 bits of accuracy.
946 
947         // [crispy] intermediate gamma levels
948         palette[i].r = gamma2table[usegamma][*doompalette++] & ~3;
949         palette[i].g = gamma2table[usegamma][*doompalette++] & ~3;
950         palette[i].b = gamma2table[usegamma][*doompalette++] & ~3;
951     }
952 
953     palette_to_set = true;
954 }
955 
956 // Given an RGB value, find the closest matching palette index.
957 
I_GetPaletteIndex(int r,int g,int b)958 int I_GetPaletteIndex(int r, int g, int b)
959 {
960     int best, best_diff, diff;
961     int i;
962 
963     best = 0; best_diff = INT_MAX;
964 
965     for (i = 0; i < 256; ++i)
966     {
967         diff = (r - palette[i].r) * (r - palette[i].r)
968              + (g - palette[i].g) * (g - palette[i].g)
969              + (b - palette[i].b) * (b - palette[i].b);
970 
971         if (diff < best_diff)
972         {
973             best = i;
974             best_diff = diff;
975         }
976 
977         if (diff == 0)
978         {
979             break;
980         }
981     }
982 
983     return best;
984 }
985 #else
I_SetPalette(int palette)986 void I_SetPalette (int palette)
987 {
988     switch (palette)
989     {
990 	case 0:
991 	    curpane = NULL;
992 	    break;
993 	case 1:
994 	case 2:
995 	case 3:
996 	case 4:
997 	case 5:
998 	case 6:
999 	case 7:
1000 	case 8:
1001 	    curpane = redpane;
1002 	    pane_alpha = 0xff * palette / 9;
1003 	    break;
1004 	case 9:
1005 	case 10:
1006 	case 11:
1007 	case 12:
1008 	    curpane = yelpane;
1009 	    pane_alpha = 0xff * (palette - 8) / 8;
1010 	    break;
1011 	case 13:
1012 	    curpane = grnpane;
1013 	    pane_alpha = 0xff * 125 / 1000;
1014 	    break;
1015 	default:
1016 	    I_Error("Unknown palette: %d!\n", palette);
1017 	    break;
1018     }
1019 }
1020 #endif
1021 
1022 //
1023 // Set the window title
1024 //
1025 
I_SetWindowTitle(const char * title)1026 void I_SetWindowTitle(const char *title)
1027 {
1028     window_title = title;
1029 }
1030 
1031 //
1032 // Call the SDL function to set the window title, based on
1033 // the title set with I_SetWindowTitle.
1034 //
1035 
I_InitWindowTitle(void)1036 void I_InitWindowTitle(void)
1037 {
1038     char *buf;
1039 
1040     buf = M_StringJoin(window_title, " - ", PACKAGE_STRING, NULL);
1041     SDL_SetWindowTitle(screen, buf);
1042     free(buf);
1043 }
1044 
1045 // Set the application icon
1046 
I_InitWindowIcon(void)1047 void I_InitWindowIcon(void)
1048 {
1049     SDL_Surface *surface;
1050 
1051     surface = SDL_CreateRGBSurfaceFrom((void *) icon_data, icon_w, icon_h,
1052                                        32, icon_w * 4,
1053                                        0xff << 24, 0xff << 16,
1054                                        0xff << 8, 0xff << 0);
1055 
1056     SDL_SetWindowIcon(screen, surface);
1057     SDL_FreeSurface(surface);
1058 }
1059 
1060 // Set video size to a particular scale factor (1x, 2x, 3x, etc.)
1061 
SetScaleFactor(int factor)1062 static void SetScaleFactor(int factor)
1063 {
1064     // Pick 320x200 or 320x240, depending on aspect ratio correct
1065 
1066     window_width = factor * SCREENWIDTH;
1067     window_height = factor * actualheight;
1068     fullscreen = false;
1069 }
1070 
I_GraphicsCheckCommandLine(void)1071 void I_GraphicsCheckCommandLine(void)
1072 {
1073     int i;
1074 
1075     //!
1076     // @category video
1077     // @vanilla
1078     //
1079     // Disable blitting the screen.
1080     //
1081 
1082     noblit = M_CheckParm ("-noblit");
1083 
1084     //!
1085     // @category video
1086     //
1087     // Don't grab the mouse when running in windowed mode.
1088     //
1089 
1090     nograbmouse_override = M_ParmExists("-nograbmouse");
1091 
1092     // default to fullscreen mode, allow override with command line
1093     // nofullscreen because we love prboom
1094 
1095     //!
1096     // @category video
1097     //
1098     // Run in a window.
1099     //
1100 
1101     if (M_CheckParm("-window") || M_CheckParm("-nofullscreen"))
1102     {
1103         fullscreen = false;
1104     }
1105 
1106     //!
1107     // @category video
1108     //
1109     // Run in fullscreen mode.
1110     //
1111 
1112     if (M_CheckParm("-fullscreen"))
1113     {
1114         fullscreen = true;
1115     }
1116 
1117     //!
1118     // @category video
1119     //
1120     // Disable the mouse.
1121     //
1122 
1123     nomouse = M_CheckParm("-nomouse") > 0;
1124 
1125     //!
1126     // @category video
1127     // @arg <x>
1128     //
1129     // Specify the screen width, in pixels. Implies -window.
1130     //
1131 
1132     i = M_CheckParmWithArgs("-width", 1);
1133 
1134     if (i > 0)
1135     {
1136         window_width = atoi(myargv[i + 1]);
1137         fullscreen = false;
1138     }
1139 
1140     //!
1141     // @category video
1142     // @arg <y>
1143     //
1144     // Specify the screen height, in pixels. Implies -window.
1145     //
1146 
1147     i = M_CheckParmWithArgs("-height", 1);
1148 
1149     if (i > 0)
1150     {
1151         window_height = atoi(myargv[i + 1]);
1152         fullscreen = false;
1153     }
1154 
1155     //!
1156     // @category video
1157     // @arg <WxY>
1158     //
1159     // Specify the dimensions of the window. Implies -window.
1160     //
1161 
1162     i = M_CheckParmWithArgs("-geometry", 1);
1163 
1164     if (i > 0)
1165     {
1166         int w, h, s;
1167 
1168         s = sscanf(myargv[i + 1], "%ix%i", &w, &h);
1169         if (s == 2)
1170         {
1171             window_width = w;
1172             window_height = h;
1173             fullscreen = false;
1174         }
1175     }
1176 
1177     //!
1178     // @category video
1179     //
1180     // Don't scale up the screen. Implies -window.
1181     //
1182 
1183     if (M_CheckParm("-1"))
1184     {
1185         SetScaleFactor(1);
1186     }
1187 
1188     //!
1189     // @category video
1190     //
1191     // Double up the screen to 2x its normal size. Implies -window.
1192     //
1193 
1194     if (M_CheckParm("-2"))
1195     {
1196         SetScaleFactor(2);
1197     }
1198 
1199     //!
1200     // @category video
1201     //
1202     // Double up the screen to 3x its normal size. Implies -window.
1203     //
1204 
1205     if (M_CheckParm("-3"))
1206     {
1207         SetScaleFactor(3);
1208     }
1209 }
1210 
1211 // Check if we have been invoked as a screensaver by xscreensaver.
1212 
I_CheckIsScreensaver(void)1213 void I_CheckIsScreensaver(void)
1214 {
1215     char *env;
1216 
1217     env = getenv("XSCREENSAVER_WINDOW");
1218 
1219     if (env != NULL)
1220     {
1221         screensaver_mode = true;
1222     }
1223 }
1224 
SetSDLVideoDriver(void)1225 static void SetSDLVideoDriver(void)
1226 {
1227     // Allow a default value for the SDL video driver to be specified
1228     // in the configuration file.
1229 
1230     if (strcmp(video_driver, "") != 0)
1231     {
1232         char *env_string;
1233 
1234         env_string = M_StringJoin("SDL_VIDEODRIVER=", video_driver, NULL);
1235         putenv(env_string);
1236         free(env_string);
1237     }
1238 }
1239 
1240 // Check the display bounds of the display referred to by 'video_display' and
1241 // set x and y to a location that places the window in the center of that
1242 // display.
CenterWindow(int * x,int * y,int w,int h)1243 static void CenterWindow(int *x, int *y, int w, int h)
1244 {
1245     SDL_Rect bounds;
1246 
1247     if (SDL_GetDisplayBounds(video_display, &bounds) < 0)
1248     {
1249         fprintf(stderr, "CenterWindow: Failed to read display bounds "
1250                         "for display #%d!\n", video_display);
1251         return;
1252     }
1253 
1254     *x = bounds.x + SDL_max((bounds.w - w) / 2, 0);
1255     *y = bounds.y + SDL_max((bounds.h - h) / 2, 0);
1256 }
1257 
I_GetWindowPosition(int * x,int * y,int w,int h)1258 void I_GetWindowPosition(int *x, int *y, int w, int h)
1259 {
1260     // Check that video_display corresponds to a display that really exists,
1261     // and if it doesn't, reset it.
1262     if (video_display < 0 || video_display >= SDL_GetNumVideoDisplays())
1263     {
1264         fprintf(stderr,
1265                 "I_GetWindowPosition: We were configured to run on display #%d, "
1266                 "but it no longer exists (max %d). Moving to display 0.\n",
1267                 video_display, SDL_GetNumVideoDisplays() - 1);
1268         video_display = 0;
1269     }
1270 
1271     // in fullscreen mode, the window "position" still matters, because
1272     // we use it to control which display we run fullscreen on.
1273 
1274     if (fullscreen)
1275     {
1276         CenterWindow(x, y, w, h);
1277         return;
1278     }
1279 
1280     // in windowed mode, the desired window position can be specified
1281     // in the configuration file.
1282 
1283     if (window_position == NULL || !strcmp(window_position, ""))
1284     {
1285         *x = *y = SDL_WINDOWPOS_UNDEFINED;
1286     }
1287     else if (!strcmp(window_position, "center"))
1288     {
1289         // Note: SDL has a SDL_WINDOWPOS_CENTER, but this is useless for our
1290         // purposes, since we also want to control which display we appear on.
1291         // So we have to do this ourselves.
1292         CenterWindow(x, y, w, h);
1293     }
1294     else if (sscanf(window_position, "%i,%i", x, y) != 2)
1295     {
1296         // invalid format: revert to default
1297         fprintf(stderr, "I_GetWindowPosition: invalid window_position setting\n");
1298         *x = *y = SDL_WINDOWPOS_UNDEFINED;
1299     }
1300 }
1301 
SetVideoMode(void)1302 static void SetVideoMode(void)
1303 {
1304     int w, h;
1305     int x, y;
1306 #ifndef CRISPY_TRUECOLOR
1307     unsigned int rmask, gmask, bmask, amask;
1308 #endif
1309     int bpp;
1310     int window_flags = 0, renderer_flags = 0;
1311     SDL_DisplayMode mode;
1312 
1313     w = window_width;
1314     h = window_height;
1315 
1316     // In windowed mode, the window can be resized while the game is
1317     // running.
1318     window_flags = SDL_WINDOW_RESIZABLE;
1319 
1320     // Set the highdpi flag - this makes a big difference on Macs with
1321     // retina displays, especially when using small window sizes.
1322     window_flags |= SDL_WINDOW_ALLOW_HIGHDPI;
1323 
1324     if (fullscreen)
1325     {
1326         if (fullscreen_width == 0 && fullscreen_height == 0)
1327         {
1328             // This window_flags means "Never change the screen resolution!
1329             // Instead, draw to the entire screen by scaling the texture
1330             // appropriately".
1331             window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
1332         }
1333         else
1334         {
1335             w = fullscreen_width;
1336             h = fullscreen_height;
1337             window_flags |= SDL_WINDOW_FULLSCREEN;
1338         }
1339     }
1340 
1341     // Running without window decorations is potentially useful if you're
1342     // playing in three window mode and want to line up three game windows
1343     // next to each other on a single desktop.
1344     // Deliberately not documented because I'm not sure how useful this is yet.
1345     if (M_ParmExists("-borderless"))
1346     {
1347         window_flags |= SDL_WINDOW_BORDERLESS;
1348     }
1349 
1350     I_GetWindowPosition(&x, &y, w, h);
1351 
1352     // Create window and renderer contexts. We set the window title
1353     // later anyway and leave the window position "undefined". If
1354     // "window_flags" contains the fullscreen flag (see above), then
1355     // w and h are ignored.
1356 
1357     if (screen == NULL)
1358     {
1359         screen = SDL_CreateWindow(NULL, x, y, w, h, window_flags);
1360 
1361         if (screen == NULL)
1362         {
1363             I_Error("Error creating window for video startup: %s",
1364             SDL_GetError());
1365         }
1366 
1367         pixel_format = SDL_GetWindowPixelFormat(screen);
1368 
1369         SDL_SetWindowMinimumSize(screen, SCREENWIDTH, actualheight);
1370 
1371         I_InitWindowTitle();
1372         I_InitWindowIcon();
1373     }
1374 
1375     // The SDL_RENDERER_TARGETTEXTURE flag is required to render the
1376     // intermediate texture into the upscaled texture.
1377     renderer_flags = SDL_RENDERER_TARGETTEXTURE;
1378 
1379     if (SDL_GetCurrentDisplayMode(video_display, &mode) != 0)
1380     {
1381         I_Error("Could not get display mode for video display #%d: %s",
1382         video_display, SDL_GetError());
1383     }
1384 
1385     // Turn on vsync if we aren't in a -timedemo
1386     if (!singletics && mode.refresh_rate > 0)
1387     {
1388       if (crispy->vsync) // [crispy] uncapped vsync
1389       {
1390         renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
1391       }
1392     }
1393 
1394     if (force_software_renderer)
1395     {
1396         renderer_flags |= SDL_RENDERER_SOFTWARE;
1397         renderer_flags &= ~SDL_RENDERER_PRESENTVSYNC;
1398         crispy->vsync = false;
1399     }
1400 
1401     if (renderer != NULL)
1402     {
1403         SDL_DestroyRenderer(renderer);
1404         // all associated textures get destroyed
1405         texture = NULL;
1406         texture_upscaled = NULL;
1407     }
1408 
1409     renderer = SDL_CreateRenderer(screen, -1, renderer_flags);
1410 
1411     // If we could not find a matching render driver,
1412     // try again without hardware acceleration.
1413 
1414     if (renderer == NULL && !force_software_renderer)
1415     {
1416         renderer_flags |= SDL_RENDERER_SOFTWARE;
1417         renderer_flags &= ~SDL_RENDERER_PRESENTVSYNC;
1418 
1419         renderer = SDL_CreateRenderer(screen, -1, renderer_flags);
1420 
1421         // If this helped, save the setting for later.
1422         if (renderer != NULL)
1423         {
1424             force_software_renderer = 1;
1425         }
1426     }
1427 
1428     if (renderer == NULL)
1429     {
1430         I_Error("Error creating renderer for screen window: %s",
1431                 SDL_GetError());
1432     }
1433 
1434     // Important: Set the "logical size" of the rendering context. At the same
1435     // time this also defines the aspect ratio that is preserved while scaling
1436     // and stretching the texture into the window.
1437 
1438     if (aspect_ratio_correct || integer_scaling)
1439     {
1440         SDL_RenderSetLogicalSize(renderer,
1441                                  SCREENWIDTH,
1442                                  actualheight);
1443     }
1444 
1445     // Force integer scales for resolution-independent rendering.
1446 
1447     SDL_RenderSetIntegerScale(renderer, integer_scaling);
1448 
1449     // Blank out the full screen area in case there is any junk in
1450     // the borders that won't otherwise be overwritten.
1451 
1452     SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
1453     SDL_RenderClear(renderer);
1454     SDL_RenderPresent(renderer);
1455 
1456 #ifndef CRISPY_TRUECOLOR
1457     // Create the 8-bit paletted and the 32-bit RGBA screenbuffer surfaces.
1458 
1459     if (screenbuffer != NULL)
1460     {
1461         SDL_FreeSurface(screenbuffer);
1462         screenbuffer = NULL;
1463     }
1464 
1465     if (screenbuffer == NULL)
1466     {
1467         screenbuffer = SDL_CreateRGBSurface(0,
1468                                             SCREENWIDTH, SCREENHEIGHT, 8,
1469                                             0, 0, 0, 0);
1470         SDL_FillRect(screenbuffer, NULL, 0);
1471     }
1472 #endif
1473 
1474     // Format of argbbuffer must match the screen pixel format because we
1475     // import the surface data into the texture.
1476 
1477     if (argbbuffer != NULL)
1478     {
1479         SDL_FreeSurface(argbbuffer);
1480         argbbuffer = NULL;
1481     }
1482 
1483     if (argbbuffer == NULL)
1484     {
1485         SDL_PixelFormatEnumToMasks(pixel_format, &bpp,
1486                                    &rmask, &gmask, &bmask, &amask);
1487         argbbuffer = SDL_CreateRGBSurface(0,
1488                                           SCREENWIDTH, SCREENHEIGHT, bpp,
1489                                           rmask, gmask, bmask, amask);
1490 #ifdef CRISPY_TRUECOLOR
1491         SDL_FillRect(argbbuffer, NULL, I_MapRGB(0xff, 0x0, 0x0));
1492         redpane = SDL_CreateTextureFromSurface(renderer, argbbuffer);
1493         SDL_SetTextureBlendMode(redpane, SDL_BLENDMODE_BLEND);
1494 
1495         SDL_FillRect(argbbuffer, NULL, I_MapRGB(0xd7, 0xba, 0x45));
1496         yelpane = SDL_CreateTextureFromSurface(renderer, argbbuffer);
1497         SDL_SetTextureBlendMode(yelpane, SDL_BLENDMODE_BLEND);
1498 
1499         SDL_FillRect(argbbuffer, NULL, I_MapRGB(0x0, 0xff, 0x0));
1500         grnpane = SDL_CreateTextureFromSurface(renderer, argbbuffer);
1501         SDL_SetTextureBlendMode(grnpane, SDL_BLENDMODE_BLEND);
1502 #endif
1503         SDL_FillRect(argbbuffer, NULL, 0);
1504     }
1505 
1506     if (texture != NULL)
1507     {
1508         SDL_DestroyTexture(texture);
1509     }
1510 
1511     // Set the scaling quality for rendering the intermediate texture into
1512     // the upscaled texture to "nearest", which is gritty and pixelated and
1513     // resembles software scaling pretty well.
1514 
1515     SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
1516 
1517     // Create the intermediate texture that the RGBA surface gets loaded into.
1518     // The SDL_TEXTUREACCESS_STREAMING flag means that this texture's content
1519     // is going to change frequently.
1520 
1521     texture = SDL_CreateTexture(renderer,
1522                                 pixel_format,
1523                                 SDL_TEXTUREACCESS_STREAMING,
1524                                 SCREENWIDTH, SCREENHEIGHT);
1525 
1526     // Initially create the upscaled texture for rendering to screen
1527 
1528     CreateUpscaledTexture(true);
1529 }
1530 
1531 // [crispy] re-calculate SCREENWIDTH, SCREENHEIGHT, NONWIDEWIDTH and WIDESCREENDELTA
I_GetScreenDimensions(void)1532 void I_GetScreenDimensions (void)
1533 {
1534 	SDL_DisplayMode mode;
1535 	int w = 16, h = 10;
1536 	int ah;
1537 
1538 	SCREENWIDTH = ORIGWIDTH << crispy->hires;
1539 	SCREENHEIGHT = ORIGHEIGHT << crispy->hires;
1540 
1541 	NONWIDEWIDTH = SCREENWIDTH;
1542 
1543 	ah = (aspect_ratio_correct == 1) ? (6 * SCREENHEIGHT / 5) : SCREENHEIGHT;
1544 
1545 	if (SDL_GetCurrentDisplayMode(video_display, &mode) == 0)
1546 	{
1547 		// [crispy] sanity check: really widescreen display?
1548 		if (mode.w * ah >= mode.h * SCREENWIDTH)
1549 		{
1550 			w = mode.w;
1551 			h = mode.h;
1552 		}
1553 	}
1554 
1555 	// [crispy] widescreen rendering makes no sense without aspect ratio correction
1556 	if (crispy->widescreen && aspect_ratio_correct)
1557 	{
1558 		switch(crispy->widescreen)
1559 		{
1560 			case RATIO_16_10:
1561 				w = 16;
1562 				h = 10;
1563 				break;
1564 			case RATIO_16_9:
1565 				w = 16;
1566 				h = 9;
1567 				break;
1568 			case RATIO_21_9:
1569 				w = 21;
1570 				h = 9;
1571 				break;
1572 			default:
1573 				break;
1574 		}
1575 
1576 		SCREENWIDTH = w * ah / h;
1577 		// [crispy] make sure SCREENWIDTH is an integer multiple of 4 ...
1578 		SCREENWIDTH = (SCREENWIDTH + (crispy->hires ? 0 : 3)) & (int)~3;
1579 		// [crispy] ... but never exceeds MAXWIDTH (array size!)
1580 		SCREENWIDTH = MIN(SCREENWIDTH, MAXWIDTH);
1581 	}
1582 
1583 	WIDESCREENDELTA = ((SCREENWIDTH - NONWIDEWIDTH) >> crispy->hires) / 2;
1584 }
1585 
I_InitGraphics(void)1586 void I_InitGraphics(void)
1587 {
1588     SDL_Event dummy;
1589 #ifndef CRISPY_TRUECOLOR
1590     byte *doompal;
1591 #endif
1592     char *env;
1593 
1594     // Pass through the XSCREENSAVER_WINDOW environment variable to
1595     // SDL_WINDOWID, to embed the SDL window into the Xscreensaver
1596     // window.
1597 
1598     env = getenv("XSCREENSAVER_WINDOW");
1599 
1600     if (env != NULL)
1601     {
1602         char winenv[30];
1603         unsigned int winid;
1604 
1605         sscanf(env, "0x%x", &winid);
1606         M_snprintf(winenv, sizeof(winenv), "SDL_WINDOWID=%u", winid);
1607 
1608         putenv(winenv);
1609     }
1610 
1611     SetSDLVideoDriver();
1612 
1613     if (SDL_Init(SDL_INIT_VIDEO) < 0)
1614     {
1615         I_Error("Failed to initialize video: %s", SDL_GetError());
1616     }
1617 
1618     // When in screensaver mode, run full screen and auto detect
1619     // screen dimensions (don't change video mode)
1620     if (screensaver_mode)
1621     {
1622         fullscreen = true;
1623     }
1624 
1625     // [crispy] run-time variable high-resolution rendering
1626     I_GetScreenDimensions();
1627 
1628 #ifndef CRISPY_TRUECOLOR
1629     blit_rect.w = SCREENWIDTH;
1630     blit_rect.h = SCREENHEIGHT;
1631 #endif
1632 
1633     // [crispy] (re-)initialize resolution-agnostic patch drawing
1634     V_Init();
1635 
1636     if (aspect_ratio_correct == 1)
1637     {
1638         actualheight = 6 * SCREENHEIGHT / 5;
1639     }
1640     else
1641     {
1642         actualheight = SCREENHEIGHT;
1643     }
1644 
1645     // Create the game window; this may switch graphic modes depending
1646     // on configuration.
1647     AdjustWindowSize();
1648     SetVideoMode();
1649 
1650 #ifndef CRISPY_TRUECOLOR
1651     // Start with a clear black screen
1652     // (screen will be flipped after we set the palette)
1653 
1654     SDL_FillRect(screenbuffer, NULL, 0);
1655 
1656     // Set the palette
1657 
1658     doompal = W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE);
1659     I_SetPalette(doompal);
1660     SDL_SetPaletteColors(screenbuffer->format->palette, palette, 0, 256);
1661 #endif
1662 
1663     // SDL2-TODO UpdateFocus();
1664     UpdateGrab();
1665 
1666     // On some systems, it takes a second or so for the screen to settle
1667     // after changing modes.  We include the option to add a delay when
1668     // setting the screen mode, so that the game doesn't start immediately
1669     // with the player unable to see anything.
1670 
1671     if (fullscreen && !screensaver_mode)
1672     {
1673         SDL_Delay(startup_delay);
1674     }
1675 
1676     // The actual 320x200 canvas that we draw to. This is the pixel buffer of
1677     // the 8-bit paletted screen buffer that gets blit on an intermediate
1678     // 32-bit RGBA screen buffer that gets loaded into a texture that gets
1679     // finally rendered into our window or full screen in I_FinishUpdate().
1680 
1681 #ifndef CRISPY_TRUECOLOR
1682     I_VideoBuffer = screenbuffer->pixels;
1683 #else
1684     I_VideoBuffer = argbbuffer->pixels;
1685 #endif
1686     V_RestoreBuffer();
1687 
1688     // Clear the screen to black.
1689 
1690     memset(I_VideoBuffer, 0, SCREENWIDTH * SCREENHEIGHT * sizeof(*I_VideoBuffer));
1691 
1692     // clear out any events waiting at the start and center the mouse
1693 
1694     while (SDL_PollEvent(&dummy));
1695 
1696     initialized = true;
1697 
1698     // Call I_ShutdownGraphics on quit
1699 
1700     I_AtExit(I_ShutdownGraphics, true);
1701 }
1702 
1703 // [crispy] re-initialize only the parts of the rendering stack that are really necessary
1704 
I_ReInitGraphics(int reinit)1705 void I_ReInitGraphics (int reinit)
1706 {
1707 	// [crispy] re-set rendering resolution and re-create framebuffers
1708 	if (reinit & REINIT_FRAMEBUFFERS)
1709 	{
1710 		unsigned int rmask, gmask, bmask, amask;
1711 		int unused_bpp;
1712 
1713 		I_GetScreenDimensions();
1714 
1715 #ifndef CRISPY_TRUECOLOR
1716 		blit_rect.w = SCREENWIDTH;
1717 		blit_rect.h = SCREENHEIGHT;
1718 #endif
1719 
1720 		// [crispy] re-initialize resolution-agnostic patch drawing
1721 		V_Init();
1722 
1723 #ifndef CRISPY_TRUECOLOR
1724 		SDL_FreeSurface(screenbuffer);
1725 		screenbuffer = SDL_CreateRGBSurface(0,
1726 				                    SCREENWIDTH, SCREENHEIGHT, 8,
1727 				                    0, 0, 0, 0);
1728 #endif
1729 
1730 		SDL_FreeSurface(argbbuffer);
1731 		SDL_PixelFormatEnumToMasks(pixel_format, &unused_bpp,
1732 		                           &rmask, &gmask, &bmask, &amask);
1733 		argbbuffer = SDL_CreateRGBSurface(0,
1734 		                                  SCREENWIDTH, SCREENHEIGHT, 32,
1735 		                                  rmask, gmask, bmask, amask);
1736 #ifndef CRISPY_TRUECOLOR
1737 		// [crispy] re-set the framebuffer pointer
1738 		I_VideoBuffer = screenbuffer->pixels;
1739 #else
1740 		I_VideoBuffer = argbbuffer->pixels;
1741 #endif
1742 		V_RestoreBuffer();
1743 
1744 		// [crispy] it will get re-created below with the new resolution
1745 		SDL_DestroyTexture(texture);
1746 	}
1747 
1748 	// [crispy] re-create renderer
1749 	if (reinit & REINIT_RENDERER)
1750 	{
1751 		SDL_RendererInfo info = {0};
1752 		int flags;
1753 
1754 		SDL_GetRendererInfo(renderer, &info);
1755 		flags = info.flags;
1756 
1757 		if (crispy->vsync && !(flags & SDL_RENDERER_SOFTWARE))
1758 		{
1759 			flags |= SDL_RENDERER_PRESENTVSYNC;
1760 		}
1761 		else
1762 		{
1763 			flags &= ~SDL_RENDERER_PRESENTVSYNC;
1764 		}
1765 
1766 		SDL_DestroyRenderer(renderer);
1767 		renderer = SDL_CreateRenderer(screen, -1, flags);
1768 		SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
1769 
1770 		// [crispy] the texture gets destroyed in SDL_DestroyRenderer(), force its re-creation
1771 		texture_upscaled = NULL;
1772 	}
1773 
1774 	// [crispy] re-create textures
1775 	if (reinit & REINIT_TEXTURES)
1776 	{
1777 		SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
1778 
1779 		texture = SDL_CreateTexture(renderer,
1780 		                            pixel_format,
1781 		                            SDL_TEXTUREACCESS_STREAMING,
1782 		                            SCREENWIDTH, SCREENHEIGHT);
1783 
1784 		// [crispy] force its re-creation
1785 		CreateUpscaledTexture(true);
1786 	}
1787 
1788 	// [crispy] re-set logical rendering resolution
1789 	if (reinit & REINIT_ASPECTRATIO)
1790 	{
1791 		if (aspect_ratio_correct == 1)
1792 		{
1793 			actualheight = 6 * SCREENHEIGHT / 5;
1794 		}
1795 		else
1796 		{
1797 			actualheight = SCREENHEIGHT;
1798 		}
1799 
1800 		if (aspect_ratio_correct || integer_scaling)
1801 		{
1802 			SDL_RenderSetLogicalSize(renderer,
1803 			                         SCREENWIDTH,
1804 			                         actualheight);
1805 		}
1806 		else
1807 		{
1808 			SDL_RenderSetLogicalSize(renderer, 0, 0);
1809 		}
1810 
1811 		#if SDL_VERSION_ATLEAST(2, 0, 5)
1812 		SDL_RenderSetIntegerScale(renderer, integer_scaling);
1813 		#endif
1814 	}
1815 
1816 	// [crispy] adjust the window size and re-set the palette
1817 	need_resize = true;
1818 }
1819 
1820 // [crispy] take screenshot of the rendered image
1821 
I_RenderReadPixels(byte ** data,int * w,int * h,int * p)1822 void I_RenderReadPixels(byte **data, int *w, int *h, int *p)
1823 {
1824 	SDL_Rect rect;
1825 	SDL_PixelFormat *format;
1826 	int temp;
1827 	uint32_t png_format;
1828 	byte *pixels;
1829 
1830 	// [crispy] adjust cropping rectangle if necessary
1831 	rect.x = rect.y = 0;
1832 	SDL_GetRendererOutputSize(renderer, &rect.w, &rect.h);
1833 	if (aspect_ratio_correct || integer_scaling)
1834 	{
1835 		if (integer_scaling)
1836 		{
1837 			int temp1, temp2, scale;
1838 			temp1 = rect.w;
1839 			temp2 = rect.h;
1840 			scale = MIN(rect.w / SCREENWIDTH, rect.h / actualheight);
1841 
1842 			rect.w = SCREENWIDTH * scale;
1843 			rect.h = actualheight * scale;
1844 
1845 			rect.x = (temp1 - rect.w) / 2;
1846 			rect.y = (temp2 - rect.h) / 2;
1847 		}
1848 		else
1849 		if (rect.w * actualheight > rect.h * SCREENWIDTH)
1850 		{
1851 			temp = rect.w;
1852 			rect.w = rect.h * SCREENWIDTH / actualheight;
1853 			rect.x = (temp - rect.w) / 2;
1854 		}
1855 		else
1856 		if (rect.h * SCREENWIDTH > rect.w * actualheight)
1857 		{
1858 			temp = rect.h;
1859 			rect.h = rect.w * actualheight / SCREENWIDTH;
1860 			rect.y = (temp - rect.h) / 2;
1861 		}
1862 	}
1863 
1864 	// [crispy] native PNG pixel format
1865 #if SDL_VERSION_ATLEAST(2, 0, 5)
1866 	png_format = SDL_PIXELFORMAT_RGB24;
1867 #else
1868 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
1869 	png_format = SDL_PIXELFORMAT_ABGR8888;
1870 #else
1871 	png_format = SDL_PIXELFORMAT_RGBA8888;
1872 #endif
1873 #endif
1874 	format = SDL_AllocFormat(png_format);
1875 	temp = rect.w * format->BytesPerPixel; // [crispy] pitch
1876 
1877 	// [crispy] As far as I understand the issue, SDL_RenderPresent()
1878 	// may return early, i.e. before it has actually finished rendering the
1879 	// current texture to screen -- from where we want to capture it.
1880 	// However, it does never return before it has finished rendering the
1881 	// *previous* texture.
1882 	// Thus, we add a second call to SDL_RenderPresent() here to make sure
1883 	// that it has at least finished rendering the previous texture, which
1884 	// already contains the scene that we actually want to capture.
1885 	if (crispy->post_rendering_hook)
1886 	{
1887 		SDL_RenderCopy(renderer, crispy->smoothscaling ? texture_upscaled : texture, NULL, NULL);
1888 		SDL_RenderPresent(renderer);
1889 	}
1890 
1891 	// [crispy] allocate memory for screenshot image
1892 	pixels = malloc(rect.h * temp);
1893 	SDL_RenderReadPixels(renderer, &rect, format->format, pixels, temp);
1894 
1895 	*data = pixels;
1896 	*w = rect.w;
1897 	*h = rect.h;
1898 	*p = temp;
1899 
1900 	SDL_FreeFormat(format);
1901 }
1902 
1903 // Bind all variables controlling video options into the configuration
1904 // file system.
I_BindVideoVariables(void)1905 void I_BindVideoVariables(void)
1906 {
1907     M_BindIntVariable("use_mouse",                 &usemouse);
1908     M_BindIntVariable("fullscreen",                &fullscreen);
1909     M_BindIntVariable("video_display",             &video_display);
1910     M_BindIntVariable("aspect_ratio_correct",      &aspect_ratio_correct);
1911     M_BindIntVariable("integer_scaling",           &integer_scaling);
1912     M_BindIntVariable("vga_porch_flash",           &vga_porch_flash);
1913     M_BindIntVariable("startup_delay",             &startup_delay);
1914     M_BindIntVariable("fullscreen_width",          &fullscreen_width);
1915     M_BindIntVariable("fullscreen_height",         &fullscreen_height);
1916     M_BindIntVariable("force_software_renderer",   &force_software_renderer);
1917     M_BindIntVariable("max_scaling_buffer_pixels", &max_scaling_buffer_pixels);
1918     M_BindIntVariable("window_width",              &window_width);
1919     M_BindIntVariable("window_height",             &window_height);
1920     M_BindIntVariable("grabmouse",                 &grabmouse);
1921     M_BindStringVariable("video_driver",           &video_driver);
1922     M_BindStringVariable("window_position",        &window_position);
1923     M_BindIntVariable("usegamma",                  &usegamma);
1924     M_BindIntVariable("png_screenshots",           &png_screenshots);
1925 }
1926 
1927 #ifdef CRISPY_TRUECOLOR
I_BlendAdd(const pixel_t bg,const pixel_t fg)1928 const pixel_t I_BlendAdd (const pixel_t bg, const pixel_t fg)
1929 {
1930 	uint32_t r, g, b;
1931 
1932 	if ((r = (fg & rmask) + (bg & rmask)) > rmask) r = rmask;
1933 	if ((g = (fg & gmask) + (bg & gmask)) > gmask) g = gmask;
1934 	if ((b = (fg & bmask) + (bg & bmask)) > bmask) b = bmask;
1935 
1936 	return amask | r | g | b;
1937 }
1938 
1939 // [crispy] http://stereopsis.com/doubleblend.html
I_BlendDark(const pixel_t bg,const int d)1940 const pixel_t I_BlendDark (const pixel_t bg, const int d)
1941 {
1942 	const uint32_t ag = (bg & 0xff00ff00) >> 8;
1943 	const uint32_t rb =  bg & 0x00ff00ff;
1944 
1945 	uint32_t sag = d * ag;
1946 	uint32_t srb = d * rb;
1947 
1948 	sag = sag & 0xff00ff00;
1949 	srb = (srb >> 8) & 0x00ff00ff;
1950 
1951 	return amask | sag | srb;
1952 }
1953 
I_BlendOver(const pixel_t bg,const pixel_t fg)1954 const pixel_t I_BlendOver (const pixel_t bg, const pixel_t fg)
1955 {
1956 	const uint32_t r = ((blend_alpha * (fg & rmask) + (0xff - blend_alpha) * (bg & rmask)) >> 8) & rmask;
1957 	const uint32_t g = ((blend_alpha * (fg & gmask) + (0xff - blend_alpha) * (bg & gmask)) >> 8) & gmask;
1958 	const uint32_t b = ((blend_alpha * (fg & bmask) + (0xff - blend_alpha) * (bg & bmask)) >> 8) & bmask;
1959 
1960 	return amask | r | g | b;
1961 }
1962 
1963 const pixel_t (*blendfunc) (const pixel_t fg, const pixel_t bg) = I_BlendOver;
1964 
I_MapRGB(const uint8_t r,const uint8_t g,const uint8_t b)1965 const pixel_t I_MapRGB (const uint8_t r, const uint8_t g, const uint8_t b)
1966 {
1967 /*
1968 	return amask |
1969 	        (((r * rmask) >> 8) & rmask) |
1970 	        (((g * gmask) >> 8) & gmask) |
1971 	        (((b * bmask) >> 8) & bmask);
1972 */
1973 	return SDL_MapRGB(argbbuffer->format, r, g, b);
1974 }
1975 #endif
1976