summaryrefslogtreecommitdiff
path: root/src/setup/display.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/setup/display.c')
-rw-r--r--src/setup/display.c754
1 files changed, 754 insertions, 0 deletions
diff --git a/src/setup/display.c b/src/setup/display.c
new file mode 100644
index 00000000..f5f190f2
--- /dev/null
+++ b/src/setup/display.c
@@ -0,0 +1,754 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2006 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _WIN32_WCE
+#include "libc_wince.h"
+#endif
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
+#include "textscreen.h"
+#include "m_config.h"
+#include "mode.h"
+
+#include "display.h"
+
+extern void RestartTextscreen(void);
+
+typedef struct
+{
+ char *description;
+ int bpp;
+} pixel_depth_t;
+
+// List of supported pixel depths.
+
+static pixel_depth_t pixel_depths[] =
+{
+ { "8-bit", 8 },
+ { "16-bit", 16 },
+ { "24-bit", 24 },
+ { "32-bit", 32 },
+};
+
+// List of strings containing supported pixel depths.
+
+static char **supported_bpps;
+static int num_supported_bpps;
+
+typedef struct
+{
+ int w, h;
+} screen_mode_t;
+
+// List of aspect ratio-uncorrected modes
+
+static screen_mode_t screen_modes_unscaled[] =
+{
+ { 320, 200 },
+ { 640, 400 },
+ { 960, 600 },
+ { 1280, 800 },
+ { 1600, 1000 },
+ { 0, 0},
+};
+
+// List of aspect ratio-corrected modes
+
+static screen_mode_t screen_modes_scaled[] =
+{
+ { 256, 200 },
+ { 320, 240 },
+ { 512, 400 },
+ { 640, 480 },
+ { 800, 600 },
+ { 960, 720 },
+ { 1024, 800 },
+ { 1280, 960 },
+ { 1280, 1000 },
+ { 1600, 1200 },
+ { 0, 0},
+};
+
+// List of fullscreen modes generated at runtime
+
+static screen_mode_t *screen_modes_fullscreen = NULL;
+static int num_screen_modes_fullscreen;
+
+static int vidmode = 0;
+
+static char *video_driver = "";
+static int autoadjust_video_settings = 1;
+static int aspect_ratio_correct = 1;
+static int fullscreen = 1;
+static int screen_width = 320;
+static int screen_height = 200;
+static int screen_bpp = 8;
+static int startup_delay = 1000;
+static int graphical_startup = 1;
+static int show_endoom = 1;
+static int usegamma = 0;
+
+// These are the last screen width/height values that were chosen by the
+// user. These are used when finding the "nearest" mode, so when
+// changing the fullscreen / aspect ratio options, the setting does not
+// jump around.
+
+static int selected_screen_width = 0, selected_screen_height;
+
+// Index into the supported_bpps of the selected pixel depth.
+
+static int selected_bpp = 0;
+
+static int system_video_env_set;
+
+// Set the SDL_VIDEODRIVER environment variable
+
+void SetDisplayDriver(void)
+{
+ static int first_time = 1;
+
+ if (first_time)
+ {
+ system_video_env_set = getenv("SDL_VIDEODRIVER") != NULL;
+
+ first_time = 0;
+ }
+
+ // Don't override the command line environment, if it has been set.
+
+ if (system_video_env_set)
+ {
+ return;
+ }
+
+ // Use the value from the configuration file, if it has been set.
+
+ if (strcmp(video_driver, "") != 0)
+ {
+ char *env_string;
+
+ env_string = malloc(strlen(video_driver) + 30);
+ sprintf(env_string, "SDL_VIDEODRIVER=%s", video_driver);
+ putenv(env_string);
+ free(env_string);
+ }
+ else
+ {
+#if defined(_WIN32) && !defined(_WIN32_WCE)
+ // On Windows, use DirectX over windib by default.
+
+ putenv("SDL_VIDEODRIVER=directx");
+#endif
+ }
+}
+
+// Query SDL as to whether any fullscreen modes are available for the
+// specified pixel depth.
+
+static int PixelDepthSupported(int bpp)
+{
+ SDL_PixelFormat format;
+ SDL_Rect **modes;
+
+ format.BitsPerPixel = bpp;
+ format.BytesPerPixel = (bpp + 7) / 8;
+
+ modes = SDL_ListModes(&format, SDL_FULLSCREEN);
+
+ return modes != NULL;
+}
+
+// Query SDL and populate the supported_bpps array.
+
+static void IdentifyPixelDepths(void)
+{
+ unsigned int i;
+ unsigned int num_depths = sizeof(pixel_depths) / sizeof(*pixel_depths);
+
+ if (supported_bpps != NULL)
+ {
+ free(supported_bpps);
+ }
+
+ supported_bpps = malloc(sizeof(char *) * num_depths);
+ num_supported_bpps = 0;
+
+ // Check each bit depth to determine if modes are available.
+
+ for (i = 0; i < num_depths; ++i)
+ {
+ // If modes are available, add this bit depth to the list.
+
+ if (PixelDepthSupported(pixel_depths[i].bpp))
+ {
+ supported_bpps[num_supported_bpps] = pixel_depths[i].description;
+ ++num_supported_bpps;
+ }
+ }
+
+ // No supported pixel depths? That's kind of a problem. Add 8bpp
+ // as a fallback.
+
+ if (num_supported_bpps == 0)
+ {
+ supported_bpps[0] = pixel_depths[0].description;
+ ++num_supported_bpps;
+ }
+}
+
+// Get the screen pixel depth corresponding to what selected_bpp is set to.
+
+static int GetSelectedBPP(void)
+{
+ unsigned int num_depths = sizeof(pixel_depths) / sizeof(*pixel_depths);
+ unsigned int i;
+
+ // Find which pixel depth is selected, and set screen_bpp.
+
+ for (i = 0; i < num_depths; ++i)
+ {
+ if (pixel_depths[i].description == supported_bpps[selected_bpp])
+ {
+ return pixel_depths[i].bpp;
+ }
+ }
+
+ // Default fallback value.
+
+ return 8;
+}
+
+// Get the index into supported_bpps of the specified pixel depth string.
+
+static int GetSupportedBPPIndex(char *description)
+{
+ unsigned int i;
+
+ for (i = 0; i < num_supported_bpps; ++i)
+ {
+ if (supported_bpps[i] == description)
+ {
+ return i;
+ }
+ }
+
+ // Shouldn't happen; fall back to the first in the list.
+
+ return 0;
+}
+
+// Set selected_bpp to match screen_bpp.
+
+static int TrySetSelectedBPP(void)
+{
+ unsigned int num_depths = sizeof(pixel_depths) / sizeof(*pixel_depths);
+ unsigned int i;
+
+ // Search pixel_depths, find the bpp that corresponds to screen_bpp,
+ // then set selected_bpp to match.
+
+ for (i = 0; i < num_depths; ++i)
+ {
+ if (pixel_depths[i].bpp == screen_bpp)
+ {
+ selected_bpp = GetSupportedBPPIndex(pixel_depths[i].description);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void SetSelectedBPP(void)
+{
+ const SDL_VideoInfo *info;
+
+ if (TrySetSelectedBPP())
+ {
+ return;
+ }
+
+ // screen_bpp does not match any supported pixel depth. Query SDL
+ // to find out what it recommends using.
+
+ info = SDL_GetVideoInfo();
+
+ if (info != NULL && info->vfmt != NULL)
+ {
+ screen_bpp = info->vfmt->BitsPerPixel;
+ }
+
+ // Try again.
+
+ if (!TrySetSelectedBPP())
+ {
+ // Give up and just use the first in the list.
+
+ selected_bpp = 0;
+ screen_bpp = GetSelectedBPP();
+ }
+}
+
+static void ModeSelected(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(mode))
+{
+ TXT_CAST_ARG(screen_mode_t, mode);
+
+ screen_width = mode->w;
+ screen_height = mode->h;
+
+ // This is now the most recently selected screen width
+
+ selected_screen_width = screen_width;
+ selected_screen_height = screen_height;
+}
+
+static int GoodFullscreenMode(screen_mode_t *mode)
+{
+ int w, h;
+
+ w = mode->w;
+ h = mode->h;
+
+ // 320x200 and 640x400 are always good (special case)
+
+ if ((w == 320 && h == 200) || (w == 640 && h == 400))
+ {
+ return 1;
+ }
+
+ // Special case: 320x240 letterboxed mode is okay (but not aspect
+ // ratio corrected 320x240)
+
+ if (w == 320 && h == 240 && !aspect_ratio_correct)
+ {
+ return 1;
+ }
+
+ // Ignore all modes less than 640x480
+
+ return w >= 640 && h >= 480;
+}
+
+// Build screen_modes_fullscreen
+
+static void BuildFullscreenModesList(void)
+{
+ SDL_PixelFormat format;
+ SDL_Rect **modes;
+ screen_mode_t *m1;
+ screen_mode_t *m2;
+ screen_mode_t m;
+ int num_modes;
+ int i;
+
+ // Free the existing modes list, if one exists
+
+ if (screen_modes_fullscreen != NULL)
+ {
+ free(screen_modes_fullscreen);
+ }
+
+ // Get a list of fullscreen modes and find out how many
+ // modes are in the list.
+
+ format.BitsPerPixel = screen_bpp;
+ format.BytesPerPixel = (screen_bpp + 7) / 8;
+
+ modes = SDL_ListModes(&format, SDL_FULLSCREEN);
+
+ if (modes == NULL || modes == (SDL_Rect **) -1)
+ {
+ num_modes = 0;
+ }
+ else
+ {
+ for (num_modes=0; modes[num_modes] != NULL; ++num_modes);
+ }
+
+ // Build the screen_modes_fullscreen array
+
+ screen_modes_fullscreen = malloc(sizeof(screen_mode_t) * (num_modes + 1));
+
+ for (i=0; i<num_modes; ++i)
+ {
+ screen_modes_fullscreen[i].w = modes[i]->w;
+ screen_modes_fullscreen[i].h = modes[i]->h;
+ }
+
+ screen_modes_fullscreen[i].w = 0;
+ screen_modes_fullscreen[i].h = 0;
+
+ // Reverse the order of the modes list (smallest modes first)
+
+ for (i=0; i<num_modes / 2; ++i)
+ {
+ m1 = &screen_modes_fullscreen[i];
+ m2 = &screen_modes_fullscreen[num_modes - 1 - i];
+
+ memcpy(&m, m1, sizeof(screen_mode_t));
+ memcpy(m1, m2, sizeof(screen_mode_t));
+ memcpy(m2, &m, sizeof(screen_mode_t));
+ }
+
+ num_screen_modes_fullscreen = num_modes;
+}
+
+static int FindBestMode(screen_mode_t *modes)
+{
+ int i;
+ int best_mode;
+ int best_mode_diff;
+ int diff;
+
+ best_mode = -1;
+ best_mode_diff = 0;
+
+ for (i=0; modes[i].w != 0; ++i)
+ {
+ if (fullscreen && !GoodFullscreenMode(&modes[i]))
+ {
+ continue;
+ }
+
+ diff = (selected_screen_width - modes[i].w)
+ * (selected_screen_width - modes[i].w)
+ + (selected_screen_height - modes[i].h)
+ * (selected_screen_height - modes[i].h);
+
+ if (best_mode == -1 || diff < best_mode_diff)
+ {
+ best_mode_diff = diff;
+ best_mode = i;
+ }
+ }
+
+ return best_mode;
+}
+
+static void GenerateModesTable(TXT_UNCAST_ARG(widget),
+ TXT_UNCAST_ARG(modes_table))
+{
+ TXT_CAST_ARG(txt_table_t, modes_table);
+ char buf[15];
+ screen_mode_t *modes;
+ txt_radiobutton_t *rbutton;
+ int i;
+
+ // Pick which modes list to use
+
+ if (fullscreen)
+ {
+ if (screen_modes_fullscreen == NULL)
+ {
+ BuildFullscreenModesList();
+ }
+
+ modes = screen_modes_fullscreen;
+ }
+ else if (aspect_ratio_correct)
+ {
+ modes = screen_modes_scaled;
+ }
+ else
+ {
+ modes = screen_modes_unscaled;
+ }
+
+ // Build the table
+
+ TXT_ClearTable(modes_table);
+ TXT_SetColumnWidths(modes_table, 14, 14, 14, 14, 14);
+
+ for (i=0; modes[i].w != 0; ++i)
+ {
+ // Skip bad fullscreen modes
+
+ if (fullscreen && !GoodFullscreenMode(&modes[i]))
+ {
+ continue;
+ }
+
+ sprintf(buf, "%ix%i", modes[i].w, modes[i].h);
+ rbutton = TXT_NewRadioButton(buf, &vidmode, i);
+ TXT_AddWidget(modes_table, rbutton);
+ TXT_SignalConnect(rbutton, "selected", ModeSelected, &modes[i]);
+ }
+
+ // Find the nearest mode in the list that matches the current
+ // settings
+
+ vidmode = FindBestMode(modes);
+
+ if (vidmode > 0)
+ {
+ screen_width = modes[vidmode].w;
+ screen_height = modes[vidmode].h;
+ }
+}
+
+// Callback invoked when the BPP selector is changed.
+
+static void UpdateBPP(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(modes_table))
+{
+ TXT_CAST_ARG(txt_table_t, modes_table);
+
+ screen_bpp = GetSelectedBPP();
+
+ // Rebuild list of fullscreen modes.
+
+ BuildFullscreenModesList();
+ GenerateModesTable(NULL, modes_table);
+}
+
+#if defined(_WIN32) && !defined(_WIN32_WCE)
+
+static int win32_video_driver = 0;
+
+static char *win32_video_drivers[] =
+{
+ "DirectX",
+ "Windows GDI",
+};
+
+static void SetWin32VideoDriver(void)
+{
+ if (!strcmp(video_driver, "windib"))
+ {
+ win32_video_driver = 1;
+ }
+ else
+ {
+ win32_video_driver = 0;
+ }
+}
+
+static void UpdateVideoDriver(TXT_UNCAST_ARG(widget),
+ TXT_UNCAST_ARG(modes_table))
+{
+ TXT_CAST_ARG(txt_table_t, modes_table);
+ char *drivers[] =
+ {
+ "",
+ "windib",
+ };
+
+ video_driver = drivers[win32_video_driver != 0];
+
+ // When the video driver is changed, we need to restart the textscreen
+ // library.
+
+ RestartTextscreen();
+
+ // Rebuild the list of supported pixel depths.
+
+ IdentifyPixelDepths();
+ SetSelectedBPP();
+
+ // Rebuild the video modes list
+
+ BuildFullscreenModesList();
+ GenerateModesTable(NULL, modes_table);
+}
+
+#endif
+
+
+void ConfigDisplay(void)
+{
+ txt_window_t *window;
+ txt_table_t *modes_table;
+ txt_table_t *bpp_table;
+ txt_checkbox_t *fs_checkbox;
+ txt_checkbox_t *ar_checkbox;
+ txt_dropdown_list_t *bpp_selector;
+ int num_columns;
+ int window_y;
+
+ // What color depths are supported? Generate supported_bpps array
+ // and set selected_bpp to match the current value of screen_bpp.
+
+ IdentifyPixelDepths();
+ SetSelectedBPP();
+
+ // First time in? Initialise selected_screen_{width,height}
+
+ if (selected_screen_width == 0)
+ {
+ selected_screen_width = screen_width;
+ selected_screen_height = screen_height;
+ }
+
+ // Open the window
+
+ window = TXT_NewWindow("Display Configuration");
+
+ TXT_AddWidgets(window,
+ fs_checkbox = TXT_NewCheckBox("Fullscreen", &fullscreen),
+ ar_checkbox = TXT_NewCheckBox("Correct aspect ratio",
+ &aspect_ratio_correct),
+ NULL);
+
+ // Some machines can have lots of video modes. This tries to
+ // keep a limit of six lines by increasing the number of
+ // columns. In extreme cases, the window is moved up slightly.
+
+ BuildFullscreenModesList();
+
+ window_y = 5;
+
+ if (num_screen_modes_fullscreen <= 18)
+ {
+ num_columns = 3;
+ }
+ else if (num_screen_modes_fullscreen <= 24)
+ {
+ num_columns = 4;
+ }
+ else
+ {
+ num_columns = 5;
+ window_y -= 3;
+ }
+
+ modes_table = TXT_NewTable(num_columns);
+
+ // The window is set at a fixed vertical position. This keeps
+ // the top of the window stationary when switching between
+ // fullscreen and windowed mode (which causes the window's
+ // height to change).
+
+ TXT_SetWindowPosition(window, TXT_HORIZ_CENTER, TXT_VERT_TOP,
+ TXT_SCREEN_W / 2, window_y);
+
+ // On Windows, there is an extra control to change between
+ // the Windows GDI and DirectX video drivers.
+
+#if defined(_WIN32) && !defined(_WIN32_WCE)
+ {
+ txt_table_t *driver_table;
+ txt_dropdown_list_t *driver_list;
+
+ driver_table = TXT_NewTable(2);
+
+ TXT_SetColumnWidths(driver_table, 20, 0);
+
+ TXT_AddWidgets(driver_table,
+ TXT_NewLabel("Video driver"),
+ driver_list = TXT_NewDropdownList(&win32_video_driver,
+ win32_video_drivers,
+ 2),
+ NULL);
+
+ TXT_SignalConnect(driver_list, "changed",
+ UpdateVideoDriver, modes_table);
+ SetWin32VideoDriver();
+
+ TXT_AddWidget(window, driver_table);
+ }
+#endif
+
+ // Screen modes list
+
+ TXT_AddWidgets(window,
+ TXT_NewSeparator("Screen mode"),
+ bpp_table = TXT_NewTable(2),
+ modes_table,
+ TXT_NewSeparator("Misc."),
+ NULL);
+
+ if (gamemission == heretic || gamemission == hexen)
+ {
+ TXT_AddWidget(window,
+ TXT_NewCheckBox("Graphical startup", &graphical_startup));
+ }
+
+ if (gamemission == doom || gamemission == heretic)
+ {
+ TXT_AddWidget(window,
+ TXT_NewCheckBox("Show ENDOOM screen", &show_endoom));
+ }
+
+ TXT_AddWidgets(bpp_table,
+ TXT_NewLabel("Color depth: "),
+ bpp_selector = TXT_NewDropdownList(&selected_bpp,
+ supported_bpps,
+ num_supported_bpps),
+ NULL);
+
+
+ TXT_SignalConnect(bpp_selector, "changed", UpdateBPP, modes_table);
+ TXT_SignalConnect(fs_checkbox, "changed", GenerateModesTable, modes_table);
+ TXT_SignalConnect(ar_checkbox, "changed", GenerateModesTable, modes_table);
+
+ GenerateModesTable(NULL, modes_table);
+}
+
+void BindDisplayVariables(void)
+{
+ M_BindVariable("autoadjust_video_settings", &autoadjust_video_settings);
+ M_BindVariable("aspect_ratio_correct", &aspect_ratio_correct);
+ M_BindVariable("fullscreen", &fullscreen);
+ M_BindVariable("screen_width", &screen_width);
+ M_BindVariable("screen_height", &screen_height);
+ M_BindVariable("startup_delay", &startup_delay);
+ M_BindVariable("video_driver", &video_driver);
+ M_BindVariable("usegamma", &usegamma);
+
+
+ if (gamemission == doom || gamemission == heretic)
+ {
+ M_BindVariable("show_endoom", &show_endoom);
+ }
+
+ if (gamemission == heretic || gamemission == hexen)
+ {
+ M_BindVariable("graphical_startup", &graphical_startup);
+ }
+
+ // Windows Vista or later? Set screen color depth to
+ // 32 bits per pixel, as 8-bit palettized screen modes
+ // don't work properly in recent versions.
+
+#if defined(_WIN32) && !defined(_WIN32_WCE)
+ {
+ OSVERSIONINFOEX version_info;
+
+ ZeroMemory(&version_info, sizeof(OSVERSIONINFOEX));
+ version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+ GetVersionEx((OSVERSIONINFO *) &version_info);
+
+ if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT
+ && version_info.dwMajorVersion >= 6)
+ {
+ screen_bpp = 32;
+ }
+ }
+#endif
+}
+