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