summaryrefslogtreecommitdiff
path: root/src/setup
diff options
context:
space:
mode:
Diffstat (limited to 'src/setup')
-rw-r--r--src/setup/.gitignore7
-rw-r--r--src/setup/Makefile.am42
-rw-r--r--src/setup/compatibility.c57
-rw-r--r--src/setup/compatibility.h29
-rw-r--r--src/setup/display.c826
-rw-r--r--src/setup/display.h30
-rw-r--r--src/setup/execute.c417
-rw-r--r--src/setup/execute.h45
-rw-r--r--src/setup/joystick.c454
-rw-r--r--src/setup/joystick.h31
-rw-r--r--src/setup/keyboard.c368
-rw-r--r--src/setup/keyboard.h29
-rw-r--r--src/setup/mainmenu.c348
-rw-r--r--src/setup/mode.c339
-rw-r--r--src/setup/mode.h37
-rw-r--r--src/setup/mouse.c169
-rw-r--r--src/setup/mouse.h29
-rw-r--r--src/setup/multiplayer.c968
-rw-r--r--src/setup/multiplayer.h36
-rw-r--r--src/setup/setup-manifest.xml.in33
-rw-r--r--src/setup/setup.desktop.in7
-rw-r--r--src/setup/setup_icon.c262
-rw-r--r--src/setup/sound.c261
-rw-r--r--src/setup/sound.h29
-rw-r--r--src/setup/txt_joybinput.c230
-rw-r--r--src/setup/txt_joybinput.h47
-rw-r--r--src/setup/txt_keyinput.c195
-rw-r--r--src/setup/txt_keyinput.h45
-rw-r--r--src/setup/txt_mouseinput.c187
-rw-r--r--src/setup/txt_mouseinput.h45
30 files changed, 5602 insertions, 0 deletions
diff --git a/src/setup/.gitignore b/src/setup/.gitignore
new file mode 100644
index 00000000..f41d11c7
--- /dev/null
+++ b/src/setup/.gitignore
@@ -0,0 +1,7 @@
+Makefile.in
+Makefile
+.deps
+setup-manifest.xml
+*.rc
+tags
+TAGS
diff --git a/src/setup/Makefile.am b/src/setup/Makefile.am
new file mode 100644
index 00000000..7fe051ee
--- /dev/null
+++ b/src/setup/Makefile.am
@@ -0,0 +1,42 @@
+
+gamesdir = $(prefix)/games
+
+AM_CFLAGS = @SDL_CFLAGS@ \
+ @SDLMIXER_CFLAGS@ \
+ -I$(top_builddir)/textscreen -I..
+
+noinst_LIBRARIES = libsetup.a
+
+SOURCE_FILES = \
+ compatibility.c compatibility.h \
+ display.c display.h \
+ joystick.c joystick.h \
+ keyboard.c keyboard.h \
+ mainmenu.c \
+ mode.c mode.h \
+ mouse.c mouse.h \
+ multiplayer.c multiplayer.h \
+ sound.c sound.h \
+ execute.c execute.h \
+ txt_joybinput.c txt_joybinput.h \
+ txt_keyinput.c txt_keyinput.h \
+ txt_mouseinput.c txt_mouseinput.h
+
+libsetup_a_SOURCES = $(SOURCE_FILES)
+
+EXTRA_DIST= \
+ setup_icon.c
+
+appdir = $(prefix)/share/applications
+app_DATA = @PROGRAM_PREFIX@setup.desktop
+
+@PROGRAM_PREFIX@setup.desktop : setup.desktop
+ cp $^ $@
+
+if HAVE_PYTHON
+
+setup_icon.c : $(top_builddir)/data/setup8.ico
+ $(top_builddir)/data/convert-icon $^ $@
+
+endif
+
diff --git a/src/setup/compatibility.c b/src/setup/compatibility.c
new file mode 100644
index 00000000..722088e8
--- /dev/null
+++ b/src/setup/compatibility.c
@@ -0,0 +1,57 @@
+// 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.
+//
+
+// Sound control menu
+
+#include <stdlib.h>
+
+#include "m_config.h"
+#include "textscreen.h"
+#include "mode.h"
+
+#include "compatibility.h"
+
+int vanilla_savegame_limit = 1;
+int vanilla_demo_limit = 1;
+
+void CompatibilitySettings(void)
+{
+ txt_window_t *window;
+
+ window = TXT_NewWindow("Compatibility");
+
+ TXT_AddWidgets(window,
+ TXT_NewCheckBox("Vanilla savegame limit",
+ &vanilla_savegame_limit),
+ TXT_NewCheckBox("Vanilla demo limit",
+ &vanilla_demo_limit),
+ NULL);
+}
+
+void BindCompatibilityVariables(void)
+{
+ if (gamemission == doom)
+ {
+ M_BindVariable("vanilla_savegame_limit", &vanilla_savegame_limit);
+ M_BindVariable("vanilla_demo_limit", &vanilla_demo_limit);
+ }
+}
+
diff --git a/src/setup/compatibility.h b/src/setup/compatibility.h
new file mode 100644
index 00000000..386a4798
--- /dev/null
+++ b/src/setup/compatibility.h
@@ -0,0 +1,29 @@
+// 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.
+//
+
+#ifndef SETUP_COMPATIBILITY_H
+#define SETUP_COMPATIBILITY_H
+
+void CompatibilitySettings(void);
+void BindCompatibilityVariables(void);
+
+#endif /* #ifndef SETUP_COMPATIBILITY_H */
+
diff --git a/src/setup/display.c b/src/setup/display.c
new file mode 100644
index 00000000..72f3c057
--- /dev/null
+++ b/src/setup/display.c
@@ -0,0 +1,826 @@
+// 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;
+ }
+ }
+
+ return -1;
+}
+
+// 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);
+
+ if (selected_bpp >= 0)
+ {
+ 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);
+}
+
+static void UpdateModeSeparator(TXT_UNCAST_ARG(widget),
+ TXT_UNCAST_ARG(separator))
+{
+ TXT_CAST_ARG(txt_separator_t, separator);
+
+ if (fullscreen)
+ {
+ TXT_SetSeparatorLabel(separator, "Screen mode");
+ }
+ else
+ {
+ TXT_SetSeparatorLabel(separator, "Window size");
+ }
+}
+
+#if defined(_WIN32) && !defined(_WIN32_WCE)
+
+static int use_directx = 1;
+
+static void SetWin32VideoDriver(void)
+{
+ if (!strcmp(video_driver, "windib"))
+ {
+ use_directx = 0;
+ }
+ else
+ {
+ use_directx = 1;
+ }
+}
+
+static void UpdateVideoDriver(TXT_UNCAST_ARG(widget),
+ TXT_UNCAST_ARG(modes_table))
+{
+ TXT_CAST_ARG(txt_table_t, modes_table);
+
+ if (use_directx)
+ {
+ video_driver = "";
+ }
+ else
+ {
+ video_driver = "windib";
+ }
+
+ // 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
+
+static void AdvancedDisplayConfig(TXT_UNCAST_ARG(widget),
+ TXT_UNCAST_ARG(modes_table))
+{
+ TXT_CAST_ARG(txt_table_t, modes_table);
+ txt_window_t *window;
+ txt_checkbox_t *ar_checkbox;
+
+ window = TXT_NewWindow("Advanced display options");
+
+ TXT_SetColumnWidths(window, 35);
+
+ TXT_AddWidgets(window,
+ ar_checkbox = TXT_NewCheckBox("Fix aspect ratio",
+ &aspect_ratio_correct),
+ 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 on exit",
+ &show_endoom));
+ }
+
+ TXT_SignalConnect(ar_checkbox, "changed", GenerateModesTable, modes_table);
+
+ // On Windows, there is an extra control to change between
+ // the Windows GDI and DirectX video drivers.
+
+#if defined(_WIN32) && !defined(_WIN32_WCE)
+ {
+ txt_radiobutton_t *dx_button, *gdi_button;
+
+ TXT_AddWidgets(window,
+ TXT_NewSeparator("Windows video driver"),
+ dx_button = TXT_NewRadioButton("DirectX",
+ &use_directx, 1),
+ gdi_button = TXT_NewRadioButton("Windows GDI",
+ &use_directx, 0),
+ NULL);
+
+ TXT_SignalConnect(dx_button, "selected",
+ UpdateVideoDriver, modes_table);
+ TXT_SignalConnect(gdi_button, "selected",
+ UpdateVideoDriver, modes_table);
+ SetWin32VideoDriver();
+ }
+#endif
+}
+
+void ConfigDisplay(void)
+{
+ txt_window_t *window;
+ txt_table_t *modes_table;
+ txt_separator_t *modes_separator;
+ txt_table_t *bpp_table;
+ txt_window_action_t *advanced_button;
+ txt_checkbox_t *fs_checkbox;
+ int i;
+ int num_columns;
+ int num_rows;
+ 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");
+
+ // 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();
+
+ if (num_screen_modes_fullscreen <= 24)
+ {
+ num_columns = 3;
+ }
+ else if (num_screen_modes_fullscreen <= 40)
+ {
+ num_columns = 4;
+ }
+ else
+ {
+ num_columns = 5;
+ }
+
+ modes_table = TXT_NewTable(num_columns);
+
+ // Build window:
+
+ TXT_AddWidget(window,
+ fs_checkbox = TXT_NewCheckBox("Full screen", &fullscreen));
+
+ if (num_supported_bpps > 1)
+ {
+ TXT_AddWidgets(window,
+ TXT_NewSeparator("Color depth"),
+ bpp_table = TXT_NewTable(4),
+ NULL);
+
+ for (i = 0; i < num_supported_bpps; ++i)
+ {
+ txt_radiobutton_t *button;
+
+ button = TXT_NewRadioButton(supported_bpps[i],
+ &selected_bpp, i);
+
+ TXT_AddWidget(bpp_table, button);
+ TXT_SignalConnect(button, "selected", UpdateBPP, modes_table);
+ }
+ }
+
+ TXT_AddWidgets(window,
+ modes_separator = TXT_NewSeparator(""),
+ modes_table,
+ NULL);
+
+ TXT_SignalConnect(fs_checkbox, "changed",
+ GenerateModesTable, modes_table);
+ TXT_SignalConnect(fs_checkbox, "changed",
+ UpdateModeSeparator, modes_separator);
+
+ // How many rows high will the configuration window be?
+ // Need to take into account number of fullscreen modes, and also
+ // number of supported pixel depths.
+ // The windowed modes list is four rows, so take the maximum of
+ // windowed and fullscreen.
+
+ num_rows = (num_screen_modes_fullscreen + num_columns - 1) / num_columns;
+
+ if (num_rows < 4)
+ {
+ num_rows = 4;
+ }
+
+ if (num_supported_bpps > 1)
+ {
+ num_rows += 2;
+ }
+
+ if (num_rows < 14)
+ {
+ window_y = 8 - ((num_rows + 1) / 2);
+ }
+ else
+ {
+ window_y = 1;
+ }
+
+ // 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);
+
+ GenerateModesTable(NULL, modes_table);
+ UpdateModeSeparator(NULL, modes_separator);
+
+ // Button to open "advanced" window.
+ // Need to pass a pointer to the modes table, as some of the options
+ // in there trigger a rebuild of it.
+
+ advanced_button = TXT_NewWindowAction('a', "Advanced");
+ TXT_SetWindowAction(window, TXT_HORIZ_CENTER, advanced_button);
+ TXT_SignalConnect(advanced_button, "pressed",
+ AdvancedDisplayConfig, 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("screen_bpp", &screen_bpp);
+ 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
+}
+
diff --git a/src/setup/display.h b/src/setup/display.h
new file mode 100644
index 00000000..2c17d8c4
--- /dev/null
+++ b/src/setup/display.h
@@ -0,0 +1,30 @@
+// 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.
+//
+
+#ifndef SETUP_DISPLAY_H
+#define SETUP_DISPLAY_H
+
+void ConfigDisplay(void);
+void SetDisplayDriver(void);
+void BindDisplayVariables(void);
+
+#endif /* #ifndef SETUP_DISPLAY_H */
+
diff --git a/src/setup/execute.c b/src/setup/execute.c
new file mode 100644
index 00000000..f85b8af4
--- /dev/null
+++ b/src/setup/execute.c
@@ -0,0 +1,417 @@
+// 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.
+//
+
+// Code for invoking Doom
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+
+#if defined(_WIN32_WCE)
+#include "libc_wince.h"
+#endif
+
+#ifdef _WIN32
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <process.h>
+
+#else
+
+#include <sys/wait.h>
+#include <unistd.h>
+
+#endif
+
+#include "textscreen.h"
+
+#include "config.h"
+#include "execute.h"
+#include "mode.h"
+#include "m_argv.h"
+#include "m_config.h"
+
+struct execute_context_s
+{
+ char *response_file;
+ FILE *stream;
+};
+
+// Returns the path to a temporary file of the given name, stored
+// inside the system temporary directory.
+
+static char *TempFile(char *s)
+{
+ char *result;
+ char *tempdir;
+
+#ifdef _WIN32
+ // Check the TEMP environment variable to find the location.
+
+ tempdir = getenv("TEMP");
+
+ if (tempdir == NULL)
+ {
+ tempdir = ".";
+ }
+#else
+ // In Unix, just use /tmp.
+
+ tempdir = "/tmp";
+#endif
+
+ result = malloc(strlen(tempdir) + strlen(s) + 2);
+ sprintf(result, "%s%c%s", tempdir, DIR_SEPARATOR, s);
+
+ return result;
+}
+
+static int ArgumentNeedsEscape(char *arg)
+{
+ char *p;
+
+ for (p = arg; *p != '\0'; ++p)
+ {
+ if (isspace(*p))
+ {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+// Arguments passed to the setup tool should be passed through to the
+// game when launching a game. Calling this adds all arguments from
+// myargv to the output context.
+
+void PassThroughArguments(execute_context_t *context)
+{
+ int i;
+
+ for (i = 1; i < myargc; ++i)
+ {
+ if (ArgumentNeedsEscape(myargv[i]))
+ {
+ AddCmdLineParameter(context, "\"%s\"", myargv[i]);
+ }
+ else
+ {
+ AddCmdLineParameter(context, "%s", myargv[i]);
+ }
+ }
+}
+
+execute_context_t *NewExecuteContext(void)
+{
+ execute_context_t *result;
+
+ result = malloc(sizeof(execute_context_t));
+
+ result->response_file = TempFile("chocolat.rsp");
+ result->stream = fopen(result->response_file, "w");
+
+ if (result->stream == NULL)
+ {
+ fprintf(stderr, "Error opening response file\n");
+ exit(-1);
+ }
+
+ return result;
+}
+
+void AddCmdLineParameter(execute_context_t *context, char *s, ...)
+{
+ va_list args;
+
+ va_start(args, s);
+
+ vfprintf(context->stream, s, args);
+ fprintf(context->stream, "\n");
+}
+
+#if defined(_WIN32)
+
+// Wait for the specified process to exit. Returns the exit code.
+
+static unsigned int WaitForProcessExit(HANDLE subprocess)
+{
+ DWORD exit_code;
+
+ for (;;)
+ {
+ WaitForSingleObject(subprocess, INFINITE);
+
+ if (!GetExitCodeProcess(subprocess, &exit_code))
+ {
+ return -1;
+ }
+
+ if (exit_code != STILL_ACTIVE)
+ {
+ return exit_code;
+ }
+ }
+}
+
+static void ConcatWCString(wchar_t *buf, const char *value)
+{
+ MultiByteToWideChar(CP_OEMCP, 0,
+ value, strlen(value) + 1,
+ buf + wcslen(buf), strlen(value) + 1);
+}
+
+// Build the command line string, a wide character string of the form:
+//
+// "program" "arg"
+
+static wchar_t *BuildCommandLine(const char *program, const char *arg)
+{
+ wchar_t exe_path[MAX_PATH];
+ wchar_t *result;
+ wchar_t *sep;
+
+ // Get the path to this .exe file.
+
+ GetModuleFileNameW(NULL, exe_path, MAX_PATH);
+
+ // Allocate buffer to contain result string.
+
+ result = calloc(wcslen(exe_path) + strlen(program) + strlen(arg) + 6,
+ sizeof(wchar_t));
+
+ wcscpy(result, L"\"");
+
+ // Copy the path part of the filename (including ending \)
+ // into the result buffer:
+
+ sep = wcsrchr(exe_path, DIR_SEPARATOR);
+
+ if (sep != NULL)
+ {
+ wcsncpy(result + 1, exe_path, sep - exe_path + 1);
+ result[sep - exe_path + 2] = '\0';
+ }
+
+ // Concatenate the name of the program:
+
+ ConcatWCString(result, program);
+
+ // End of program name, start of argument:
+
+ wcscat(result, L"\" \"");
+
+ ConcatWCString(result, arg);
+
+ wcscat(result, L"\"");
+
+ return result;
+}
+
+static int ExecuteCommand(const char *program, const char *arg)
+{
+ STARTUPINFOW startup_info;
+ PROCESS_INFORMATION proc_info;
+ wchar_t *command;
+ int result = 0;
+
+ command = BuildCommandLine(program, arg);
+
+ // Invoke the program:
+
+ memset(&proc_info, 0, sizeof(proc_info));
+ memset(&startup_info, 0, sizeof(startup_info));
+ startup_info.cb = sizeof(startup_info);
+
+ if (!CreateProcessW(NULL, command,
+ NULL, NULL, FALSE, 0, NULL, NULL,
+ &startup_info, &proc_info))
+ {
+ result = -1;
+ }
+ else
+ {
+ // Wait for the process to finish, and save the exit code.
+
+ result = WaitForProcessExit(proc_info.hProcess);
+
+ CloseHandle(proc_info.hProcess);
+ CloseHandle(proc_info.hThread);
+ }
+
+ free(command);
+
+ return result;
+}
+
+#else
+
+// Given the specified program name, get the full path to the program,
+// assuming that it is in the same directory as this program is.
+
+static char *GetFullExePath(const char *program)
+{
+ char *result;
+ char *sep;
+ unsigned int path_len;
+
+ sep = strrchr(myargv[0], DIR_SEPARATOR);
+
+ if (sep == NULL)
+ {
+ result = strdup(program);
+ }
+ else
+ {
+ path_len = sep - myargv[0] + 1;
+
+ result = malloc(strlen(program) + path_len + 1);
+
+ strncpy(result, myargv[0], path_len);
+ result[path_len] = '\0';
+
+ strcat(result, program);
+ }
+
+ return result;
+}
+
+static int ExecuteCommand(const char *program, const char *arg)
+{
+ pid_t childpid;
+ int result;
+ const char *argv[3];
+
+ childpid = fork();
+
+ if (childpid == 0)
+ {
+ // This is the child. Execute the command.
+
+ argv[0] = GetFullExePath(program);
+ argv[1] = arg;
+ argv[2] = NULL;
+
+ execvp(argv[0], (char **) argv);
+
+ exit(0x80);
+ }
+ else
+ {
+ // This is the parent. Wait for the child to finish, and return
+ // the status code.
+
+ waitpid(childpid, &result, 0);
+
+ if (WIFEXITED(result) && WEXITSTATUS(result) != 0x80)
+ {
+ return WEXITSTATUS(result);
+ }
+ else
+ {
+ return -1;
+ }
+ }
+}
+
+#endif
+
+int ExecuteDoom(execute_context_t *context)
+{
+ char *response_file_arg;
+ int result;
+
+ fclose(context->stream);
+
+ // Build the command line
+
+ response_file_arg = malloc(strlen(context->response_file) + 2);
+ sprintf(response_file_arg, "@%s", context->response_file);
+
+ // Run Doom
+
+ result = ExecuteCommand(GetExecutableName(), response_file_arg);
+
+ free(response_file_arg);
+
+ // Destroy context
+ remove(context->response_file);
+ free(context->response_file);
+ free(context);
+
+ return result;
+}
+
+static void TestCallback(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(data))
+{
+ execute_context_t *exec;
+ char *main_cfg;
+ char *extra_cfg;
+ txt_window_t *testwindow;
+ txt_label_t *label;
+
+ testwindow = TXT_NewWindow("Starting Doom");
+
+ label = TXT_NewLabel("Starting Doom to test the\n"
+ "settings. Please wait.");
+ TXT_SetWidgetAlign(label, TXT_HORIZ_CENTER);
+ TXT_AddWidget(testwindow, label);
+ TXT_DrawDesktop();
+
+ // Save temporary configuration files with the current configuration
+
+ main_cfg = TempFile("tmp.cfg");
+ extra_cfg = TempFile("extratmp.cfg");
+
+ M_SaveDefaultsAlternate(main_cfg, extra_cfg);
+
+ // Run with the -testcontrols parameter
+
+ exec = NewExecuteContext();
+ AddCmdLineParameter(exec, "-testcontrols");
+ AddCmdLineParameter(exec, "-config \"%s\"", main_cfg);
+ AddCmdLineParameter(exec, "-extraconfig \"%s\"", extra_cfg);
+ ExecuteDoom(exec);
+
+ TXT_CloseWindow(testwindow);
+
+ // Delete the temporary config files
+
+ remove(main_cfg);
+ remove(extra_cfg);
+ free(main_cfg);
+ free(extra_cfg);
+}
+
+txt_window_action_t *TestConfigAction(void)
+{
+ txt_window_action_t *test_action;
+
+ test_action = TXT_NewWindowAction('t', "Test");
+ TXT_SignalConnect(test_action, "pressed", TestCallback, NULL);
+
+ return test_action;
+}
+
diff --git a/src/setup/execute.h b/src/setup/execute.h
new file mode 100644
index 00000000..25f1f10a
--- /dev/null
+++ b/src/setup/execute.h
@@ -0,0 +1,45 @@
+// 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.
+//
+
+#ifndef TESTCONFIG_H
+#define TESTCONFIG_H
+
+#include "textscreen.h"
+
+typedef struct execute_context_s execute_context_t;
+
+#define IWAD_DOOM2 (1 << 0) /* doom2.wad */
+#define IWAD_PLUTONIA (1 << 1) /* plutonia.wad */
+#define IWAD_TNT (1 << 2) /* tnt.wad */
+#define IWAD_DOOM (1 << 3) /* doom.wad */
+#define IWAD_DOOM1 (1 << 4) /* doom1.wad */
+#define IWAD_CHEX (1 << 5) /* chex.wad */
+
+execute_context_t *NewExecuteContext(void);
+void AddCmdLineParameter(execute_context_t *context, char *s, ...);
+void PassThroughArguments(execute_context_t *context);
+int ExecuteDoom(execute_context_t *context);
+int FindInstalledIWADs(void);
+
+txt_window_action_t *TestConfigAction(void);
+
+#endif /* #ifndef TESTCONFIG_H */
+
diff --git a/src/setup/joystick.c b/src/setup/joystick.c
new file mode 100644
index 00000000..2a7b1f79
--- /dev/null
+++ b/src/setup/joystick.c
@@ -0,0 +1,454 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2007 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 <stdio.h>
+#include <stdlib.h>
+
+#include "doomtype.h"
+#include "m_config.h"
+#include "m_controls.h"
+#include "textscreen.h"
+
+#include "execute.h"
+#include "joystick.h"
+#include "mode.h"
+#include "txt_joybinput.h"
+
+typedef enum
+{
+ CALIBRATE_CENTER,
+ CALIBRATE_LEFT,
+ CALIBRATE_UP,
+} calibration_stage_t;
+
+// SDL joystick successfully initialized?
+
+static int joystick_initted = 0;
+
+// Joystick enable/disable
+
+static int usejoystick = 0;
+
+// Joystick to use, as an SDL joystick index:
+
+int joystick_index = -1;
+
+// Which joystick axis to use for horizontal movement, and whether to
+// invert the direction:
+
+static int joystick_x_axis = 0;
+static int joystick_x_invert = 0;
+
+// Which joystick axis to use for vertical movement, and whether to
+// invert the direction:
+
+static int joystick_y_axis = 1;
+static int joystick_y_invert = 0;
+
+static txt_button_t *joystick_button;
+
+static int *all_joystick_buttons[] = {
+ &joybstraferight, &joybstrafeleft, &joybfire, &joybspeed,
+ &joybuse, &joybstrafe, &joybprevweapon, &joybnextweapon, &joybjump
+};
+
+//
+// Calibration
+//
+
+static txt_window_t *calibration_window;
+static txt_label_t *calibration_label;
+static calibration_stage_t calibrate_stage;
+static SDL_Joystick **all_joysticks = NULL;
+
+// Set the label showing the name of the currently selected joystick
+
+static void SetJoystickButtonLabel(void)
+{
+ char *name;
+
+ name = "None set";
+
+ if (joystick_initted
+ && joystick_index >= 0 && joystick_index < SDL_NumJoysticks())
+ {
+ name = (char *) SDL_JoystickName(joystick_index);
+ }
+
+ TXT_SetButtonLabel(joystick_button, name);
+}
+
+// Try to open all joysticks visible to SDL.
+
+static int OpenAllJoysticks(void)
+{
+ int i;
+ int num_joysticks;
+ int result;
+
+ if (!joystick_initted)
+ {
+ return 0;
+ }
+
+ // SDL_JoystickOpen() all joysticks.
+
+ num_joysticks = SDL_NumJoysticks();
+
+ all_joysticks = malloc(sizeof(SDL_Joystick *) * num_joysticks);
+
+ result = 0;
+
+ for (i=0; i<num_joysticks; ++i)
+ {
+ all_joysticks[i] = SDL_JoystickOpen(i);
+
+ // If any joystick is successfully opened, return true.
+
+ if (all_joysticks[i] != NULL)
+ {
+ result = 1;
+ }
+ }
+
+ // Success? Turn on joystick events.
+
+ if (result)
+ {
+ SDL_JoystickEventState(SDL_ENABLE);
+ }
+ else
+ {
+ free(all_joysticks);
+ all_joysticks = NULL;
+ }
+
+ return result;
+}
+
+// Close all the joysticks opened with OpenAllJoysticks()
+
+static void CloseAllJoysticks(void)
+{
+ int i;
+ int num_joysticks;
+
+ num_joysticks = SDL_NumJoysticks();
+
+ for (i=0; i<num_joysticks; ++i)
+ {
+ if (all_joysticks[i] != NULL)
+ {
+ SDL_JoystickClose(all_joysticks[i]);
+ }
+ }
+
+ SDL_JoystickEventState(SDL_DISABLE);
+
+ free(all_joysticks);
+ all_joysticks = NULL;
+}
+
+static void SetCalibrationLabel(void)
+{
+ char *message = "???";
+
+ switch (calibrate_stage)
+ {
+ case CALIBRATE_CENTER:
+ message = "Move the joystick to the\n"
+ "center, and press a button.";
+ break;
+ case CALIBRATE_UP:
+ message = "Move the joystick up,\n"
+ "and press a button.";
+ break;
+ case CALIBRATE_LEFT:
+ message = "Move the joystick to the\n"
+ "left, and press a button.";
+ break;
+ }
+
+ TXT_SetLabel(calibration_label, message);
+}
+
+static void CalibrateAxis(int *axis_index, int *axis_invert)
+{
+ SDL_Joystick *joystick;
+ int best_axis;
+ int best_value;
+ int best_invert;
+ Sint16 axis_value;
+ int i;
+
+ joystick = all_joysticks[joystick_index];
+
+ // Check all axes to find which axis has the largest value. We test
+ // for one axis at a time, so eg. when we prompt to push the joystick
+ // left, whichever axis has the largest value is the left axis.
+
+ best_axis = 0;
+ best_value = 0;
+ best_invert = 0;
+
+ for (i=0; i<SDL_JoystickNumAxes(joystick); ++i)
+ {
+ axis_value = SDL_JoystickGetAxis(joystick, i);
+
+ if (abs(axis_value) > best_value)
+ {
+ best_value = abs(axis_value);
+ best_invert = axis_value > 0;
+ best_axis = i;
+ }
+ }
+
+ // Save the best values we have found
+
+ *axis_index = best_axis;
+ *axis_invert = best_invert;
+}
+
+static int CalibrationEventCallback(SDL_Event *event, void *user_data)
+{
+ if (event->type == SDL_JOYBUTTONDOWN
+ && (joystick_index == -1 || event->jbutton.which == joystick_index))
+ {
+ switch (calibrate_stage)
+ {
+ case CALIBRATE_CENTER:
+ // Centering stage selects which joystick to use.
+ joystick_index = event->jbutton.which;
+ break;
+
+ case CALIBRATE_LEFT:
+ CalibrateAxis(&joystick_x_axis, &joystick_x_invert);
+ break;
+
+ case CALIBRATE_UP:
+ CalibrateAxis(&joystick_y_axis, &joystick_y_invert);
+ break;
+ }
+
+ if (calibrate_stage == CALIBRATE_UP)
+ {
+ // Final stage; close the window
+
+ TXT_CloseWindow(calibration_window);
+ }
+ else
+ {
+ // Advance to the next calibration stage
+
+ ++calibrate_stage;
+ SetCalibrationLabel();
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static void NoJoystick(void)
+{
+ txt_window_t *window;
+
+ window = TXT_NewWindow(NULL);
+
+ TXT_AddWidget(window,
+ TXT_NewLabel("No joysticks could be opened.\n\n"
+ "Try configuring your joystick from within\n"
+ "your OS first."));
+
+ TXT_SetWindowAction(window, TXT_HORIZ_LEFT, NULL);
+ TXT_SetWindowAction(window, TXT_HORIZ_CENTER,
+ TXT_NewWindowEscapeAction(window));
+ TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, NULL);
+
+ joystick_index = -1;
+ SetJoystickButtonLabel();
+}
+
+static void CalibrateWindowClosed(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
+{
+ CloseAllJoysticks();
+ TXT_SDL_SetEventCallback(NULL, NULL);
+ SetJoystickButtonLabel();
+}
+
+static void CalibrateJoystick(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
+{
+ calibrate_stage = CALIBRATE_CENTER;
+
+ // Try to open all available joysticks. If none are opened successfully,
+ // bomb out with an error.
+
+ if (!OpenAllJoysticks())
+ {
+ NoJoystick();
+ return;
+ }
+
+ calibration_window = TXT_NewWindow("Joystick calibration");
+
+ TXT_AddWidgets(calibration_window,
+ TXT_NewLabel("Please follow the following instructions\n"
+ "in order to calibrate your joystick."),
+ TXT_NewStrut(0, 1),
+ calibration_label = TXT_NewLabel("zzz"),
+ TXT_NewStrut(0, 1),
+ NULL);
+
+ TXT_SetWindowAction(calibration_window, TXT_HORIZ_LEFT, NULL);
+ TXT_SetWindowAction(calibration_window, TXT_HORIZ_CENTER,
+ TXT_NewWindowAbortAction(calibration_window));
+ TXT_SetWindowAction(calibration_window, TXT_HORIZ_RIGHT, NULL);
+
+ TXT_SetWidgetAlign(calibration_label, TXT_HORIZ_CENTER);
+ TXT_SDL_SetEventCallback(CalibrationEventCallback, NULL);
+
+ TXT_SignalConnect(calibration_window, "closed", CalibrateWindowClosed, NULL);
+
+ // Start calibration
+
+ joystick_index = -1;
+ calibrate_stage = CALIBRATE_CENTER;
+
+ SetCalibrationLabel();
+}
+
+void JoyButtonSetCallback(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(variable))
+{
+ TXT_CAST_ARG(int, variable);
+ unsigned int i;
+
+ // Only allow a button to be bound to one action at a time. If
+ // we assign a key that another action is using, set that other action
+ // to -1.
+
+ for (i=0; i<arrlen(all_joystick_buttons); ++i)
+ {
+ if (variable != all_joystick_buttons[i]
+ && *variable == *all_joystick_buttons[i])
+ {
+ *all_joystick_buttons[i] = -1;
+ }
+ }
+}
+
+
+//
+// GUI
+//
+
+static void JoystickWindowClosed(TXT_UNCAST_ARG(window), TXT_UNCAST_ARG(unused))
+{
+ if (joystick_initted)
+ {
+ SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
+ joystick_initted = 0;
+ }
+}
+
+static void AddJoystickControl(txt_table_t *table, char *label, int *var)
+{
+ txt_joystick_input_t *joy_input;
+
+ joy_input = TXT_NewJoystickInput(var);
+
+ TXT_AddWidget(table, TXT_NewLabel(label));
+ TXT_AddWidget(table, joy_input);
+
+ TXT_SignalConnect(joy_input, "set", JoyButtonSetCallback, var);
+}
+
+void ConfigJoystick(void)
+{
+ txt_window_t *window;
+ txt_table_t *button_table;
+ txt_table_t *joystick_table;
+
+ if (!joystick_initted)
+ {
+ joystick_initted = SDL_Init(SDL_INIT_JOYSTICK) >= 0;
+ }
+
+ window = TXT_NewWindow("Joystick configuration");
+
+ TXT_AddWidgets(window,
+ TXT_NewCheckBox("Enable joystick", &usejoystick),
+ joystick_table = TXT_NewTable(2),
+ TXT_NewSeparator("Joystick buttons"),
+ button_table = TXT_NewTable(2),
+ NULL);
+
+ TXT_SetColumnWidths(joystick_table, 20, 15);
+
+ TXT_AddWidgets(joystick_table,
+ TXT_NewLabel("Current joystick"),
+ joystick_button = TXT_NewButton("zzzz"),
+ NULL);
+
+ TXT_SetColumnWidths(button_table, 20, 15);
+
+ AddJoystickControl(button_table, "Fire/Attack", &joybfire);
+ AddJoystickControl(button_table, "Use", &joybuse);
+
+ // High values of joybspeed are used to activate the "always run mode"
+ // trick in Vanilla Doom. If this has been enabled, not only is the
+ // joybspeed value meaningless, but the control itself is useless.
+
+ if (joybspeed < 20)
+ {
+ AddJoystickControl(button_table, "Speed", &joybspeed);
+ }
+
+ AddJoystickControl(button_table, "Strafe", &joybstrafe);
+
+ AddJoystickControl(button_table, "Strafe Left", &joybstrafeleft);
+ AddJoystickControl(button_table, "Strafe Right", &joybstraferight);
+ AddJoystickControl(button_table, "Previous weapon", &joybprevweapon);
+ AddJoystickControl(button_table, "Next weapon", &joybnextweapon);
+
+ if (gamemission == hexen)
+ {
+ AddJoystickControl(button_table, "Jump", &joybjump);
+ }
+
+ TXT_SignalConnect(joystick_button, "pressed", CalibrateJoystick, NULL);
+ TXT_SignalConnect(window, "closed", JoystickWindowClosed, NULL);
+
+ TXT_SetWindowAction(window, TXT_HORIZ_CENTER, TestConfigAction());
+
+ SetJoystickButtonLabel();
+}
+
+void BindJoystickVariables(void)
+{
+ M_BindVariable("use_joystick", &usejoystick);
+ M_BindVariable("joystick_index", &joystick_index);
+ M_BindVariable("joystick_x_axis", &joystick_x_axis);
+ M_BindVariable("joystick_y_axis", &joystick_y_axis);
+ M_BindVariable("joystick_x_invert", &joystick_x_invert);
+ M_BindVariable("joystick_y_invert", &joystick_y_invert);
+}
+
diff --git a/src/setup/joystick.h b/src/setup/joystick.h
new file mode 100644
index 00000000..b6dd09b9
--- /dev/null
+++ b/src/setup/joystick.h
@@ -0,0 +1,31 @@
+// 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.
+//
+
+#ifndef SETUP_JOYSTICK_H
+#define SETUP_JOYSTICK_H
+
+extern int joystick_index;
+
+void ConfigJoystick(void);
+void BindJoystickVariables(void);
+
+#endif /* #ifndef SETUP_JOYSTICK_H */
+
diff --git a/src/setup/keyboard.c b/src/setup/keyboard.c
new file mode 100644
index 00000000..a128b3ab
--- /dev/null
+++ b/src/setup/keyboard.c
@@ -0,0 +1,368 @@
+// 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 "textscreen.h"
+#include "doomtype.h"
+#include "m_config.h"
+#include "m_controls.h"
+
+#include "execute.h"
+#include "txt_keyinput.h"
+
+#include "mode.h"
+#include "joystick.h"
+#include "keyboard.h"
+
+static int vanilla_keyboard_mapping = 1;
+
+static int always_run = 0;
+
+// Keys within these groups cannot have the same value.
+
+static int *controls[] = { &key_left, &key_right, &key_up, &key_down,
+ &key_strafeleft, &key_straferight, &key_fire,
+ &key_use, &key_strafe, &key_speed, &key_jump,
+ &key_flyup, &key_flydown, &key_flycenter,
+ &key_lookup, &key_lookdown, &key_lookcenter,
+ &key_invleft, &key_invright, &key_useartifact,
+ &key_pause,
+ &key_weapon1, &key_weapon2, &key_weapon3,
+ &key_weapon4, &key_weapon5, &key_weapon6,
+ &key_weapon7, &key_weapon8,
+ &key_prevweapon, &key_nextweapon, NULL };
+
+static int *menu_nav[] = { &key_menu_activate, &key_menu_up, &key_menu_down,
+ &key_menu_left, &key_menu_right, &key_menu_back,
+ &key_menu_forward, NULL };
+
+static int *shortcuts[] = { &key_menu_help, &key_menu_save, &key_menu_load,
+ &key_menu_volume, &key_menu_detail, &key_menu_qsave,
+ &key_menu_endgame, &key_menu_messages, &key_spy,
+ &key_menu_qload, &key_menu_quit, &key_menu_gamma,
+ &key_menu_incscreen, &key_menu_decscreen,
+ &key_message_refresh, &key_multi_msg,
+ &key_multi_msgplayer[0], &key_multi_msgplayer[1],
+ &key_multi_msgplayer[2], &key_multi_msgplayer[3] };
+
+static int *map_keys[] = { &key_map_north, &key_map_south, &key_map_east,
+ &key_map_west, &key_map_zoomin, &key_map_zoomout,
+ &key_map_toggle, &key_map_maxzoom, &key_map_follow,
+ &key_map_grid, &key_map_mark, &key_map_clearmark,
+ NULL };
+
+static void UpdateJoybSpeed(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(var))
+{
+ if (always_run)
+ {
+ /*
+ <Janizdreg> if you want to pick one for chocolate doom to use,
+ pick 29, since that is the most universal one that
+ also works with heretic, hexen and strife =P
+
+ NB. This choice also works with original, ultimate and final exes.
+ */
+
+ joybspeed = 29;
+ }
+ else
+ {
+ joybspeed = 0;
+ }
+}
+
+static int VarInGroup(int *variable, int **group)
+{
+ unsigned int i;
+
+ for (i=0; group[i] != NULL; ++i)
+ {
+ if (group[i] == variable)
+ {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void CheckKeyGroup(int *variable, int **group)
+{
+ unsigned int i;
+
+ // Don't check unless the variable is in this group.
+
+ if (!VarInGroup(variable, group))
+ {
+ return;
+ }
+
+ // If another variable has the same value as the new value, reset it.
+
+ for (i=0; group[i] != NULL; ++i)
+ {
+ if (*variable == *group[i] && group[i] != variable)
+ {
+ // A different key has the same value. Clear the existing
+ // value. This ensures that no two keys can have the same
+ // value.
+
+ *group[i] = 0;
+ }
+ }
+}
+
+// Callback invoked when a key control is set
+
+static void KeySetCallback(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(variable))
+{
+ TXT_CAST_ARG(int, variable);
+
+ CheckKeyGroup(variable, controls);
+ CheckKeyGroup(variable, menu_nav);
+ CheckKeyGroup(variable, shortcuts);
+ CheckKeyGroup(variable, map_keys);
+}
+
+// Add a label and keyboard input to the specified table.
+
+static void AddKeyControl(txt_table_t *table, char *name, int *var)
+{
+ txt_key_input_t *key_input;
+
+ TXT_AddWidget(table, TXT_NewLabel(name));
+ key_input = TXT_NewKeyInput(var);
+ TXT_AddWidget(table, key_input);
+
+ TXT_SignalConnect(key_input, "set", KeySetCallback, var);
+}
+
+static void AddSectionLabel(txt_table_t *table, char *title, boolean add_space)
+{
+ char buf[64];
+
+ if (add_space)
+ {
+ TXT_AddWidgets(table, TXT_NewStrut(0, 1), TXT_NewStrut(0, 1),
+ NULL);
+ }
+
+ sprintf(buf, " - %s - ", title);
+
+ TXT_AddWidgets(table, TXT_NewLabel(buf), TXT_NewStrut(0, 0),
+ NULL);
+}
+static void ConfigExtraKeys(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
+{
+ txt_window_t *window;
+ txt_scrollpane_t *scrollpane;
+ txt_table_t *table;
+ boolean extra_keys = gamemission == heretic || gamemission == hexen;
+
+ window = TXT_NewWindow("Extra keyboard controls");
+
+ table = TXT_NewTable(2);
+
+ TXT_SetColumnWidths(table, 20, 9);
+
+ if (extra_keys)
+ {
+ // When we have extra controls, a scrollable pane must be used.
+
+ scrollpane = TXT_NewScrollPane(0, 13, table);
+ TXT_AddWidget(window, scrollpane);
+
+ AddSectionLabel(table, "View", false);
+
+ AddKeyControl(table, "Look up", &key_lookup);
+ AddKeyControl(table, "Look down", &key_lookdown);
+ AddKeyControl(table, "Center view", &key_lookcenter);
+
+ AddSectionLabel(table, "Flying", true);
+
+ AddKeyControl(table, "Fly up", &key_flyup);
+ AddKeyControl(table, "Fly down", &key_flydown);
+ AddKeyControl(table, "Fly center", &key_flycenter);
+
+ AddSectionLabel(table, "Inventory", true);
+
+ AddKeyControl(table, "Inventory left", &key_invleft);
+ AddKeyControl(table, "Inventory right", &key_invright);
+ AddKeyControl(table, "Use artifact", &key_useartifact);
+ }
+ else
+ {
+ TXT_AddWidget(window, table);
+ }
+
+ AddSectionLabel(table, "Weapons", extra_keys);
+
+ AddKeyControl(table, "Weapon 1", &key_weapon1);
+ AddKeyControl(table, "Weapon 2", &key_weapon2);
+ AddKeyControl(table, "Weapon 3", &key_weapon3);
+ AddKeyControl(table, "Weapon 4", &key_weapon4);
+ AddKeyControl(table, "Weapon 5", &key_weapon5);
+ AddKeyControl(table, "Weapon 6", &key_weapon6);
+ AddKeyControl(table, "Weapon 7", &key_weapon7);
+ AddKeyControl(table, "Weapon 8", &key_weapon8);
+ AddKeyControl(table, "Previous weapon", &key_prevweapon);
+ AddKeyControl(table, "Next weapon", &key_nextweapon);
+}
+
+static void OtherKeysDialog(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
+{
+ txt_window_t *window;
+ txt_table_t *table;
+ txt_scrollpane_t *scrollpane;
+
+ window = TXT_NewWindow("Other keys");
+
+ table = TXT_NewTable(2);
+
+ TXT_SetColumnWidths(table, 25, 9);
+
+ AddSectionLabel(table, "Menu navigation", false);
+
+ AddKeyControl(table, "Activate menu", &key_menu_activate);
+ AddKeyControl(table, "Move cursor up", &key_menu_up);
+ AddKeyControl(table, "Move cursor down", &key_menu_down);
+ AddKeyControl(table, "Move slider left", &key_menu_left);
+ AddKeyControl(table, "Move slider right", &key_menu_right);
+ AddKeyControl(table, "Go to previous menu", &key_menu_back);
+ AddKeyControl(table, "Activate menu item", &key_menu_forward);
+ AddKeyControl(table, "Confirm action", &key_menu_confirm);
+ AddKeyControl(table, "Cancel action", &key_menu_abort);
+
+ AddSectionLabel(table, "Shortcut keys", true);
+
+ AddKeyControl(table, "Pause game", &key_pause);
+ AddKeyControl(table, "Help screen", &key_menu_help);
+ AddKeyControl(table, "Save game", &key_menu_save);
+ AddKeyControl(table, "Load game", &key_menu_load);
+ AddKeyControl(table, "Sound volume", &key_menu_volume);
+ AddKeyControl(table, "Toggle detail", &key_menu_detail);
+ AddKeyControl(table, "Quick save", &key_menu_qsave);
+ AddKeyControl(table, "End game", &key_menu_endgame);
+ AddKeyControl(table, "Toggle messages", &key_menu_messages);
+ AddKeyControl(table, "Quick load", &key_menu_qload);
+ AddKeyControl(table, "Quit game", &key_menu_quit);
+ AddKeyControl(table, "Toggle gamma", &key_menu_gamma);
+ AddKeyControl(table, "Multiplayer spy", &key_spy);
+
+ AddKeyControl(table, "Increase screen size", &key_menu_incscreen);
+ AddKeyControl(table, "Decrease screen size", &key_menu_decscreen);
+
+ AddKeyControl(table, "Display last message", &key_message_refresh);
+ AddKeyControl(table, "Finish recording demo", &key_demo_quit);
+
+ AddSectionLabel(table, "Map", true);
+ AddKeyControl(table, "Toggle map", &key_map_toggle);
+ AddKeyControl(table, "Zoom in", &key_map_zoomin);
+ AddKeyControl(table, "Zoom out", &key_map_zoomout);
+ AddKeyControl(table, "Maximum zoom out", &key_map_maxzoom);
+ AddKeyControl(table, "Follow mode", &key_map_follow);
+ AddKeyControl(table, "Pan north", &key_map_north);
+ AddKeyControl(table, "Pan south", &key_map_south);
+ AddKeyControl(table, "Pan east", &key_map_east);
+ AddKeyControl(table, "Pan west", &key_map_west);
+ AddKeyControl(table, "Toggle grid", &key_map_grid);
+ AddKeyControl(table, "Mark location", &key_map_mark);
+ AddKeyControl(table, "Clear all marks", &key_map_clearmark);
+
+ AddSectionLabel(table, "Multiplayer", true);
+
+ AddKeyControl(table, "Send message", &key_multi_msg);
+ AddKeyControl(table, "- to green", &key_multi_msgplayer[0]);
+ AddKeyControl(table, "- to indigo", &key_multi_msgplayer[1]);
+ AddKeyControl(table, "- to brown", &key_multi_msgplayer[2]);
+ AddKeyControl(table, "- to red", &key_multi_msgplayer[3]);
+
+ scrollpane = TXT_NewScrollPane(0, 13, table);
+
+ TXT_AddWidget(window, scrollpane);
+}
+
+void ConfigKeyboard(void)
+{
+ txt_window_t *window;
+ txt_table_t *movement_table;
+ txt_table_t *action_table;
+ txt_table_t *dialogs_table;
+ txt_checkbox_t *run_control;
+
+ always_run = joybspeed >= 20;
+
+ window = TXT_NewWindow("Keyboard configuration");
+
+ TXT_AddWidgets(window,
+ TXT_NewSeparator("Movement"),
+ movement_table = TXT_NewTable(4),
+
+ TXT_NewSeparator("Action"),
+ action_table = TXT_NewTable(4),
+ dialogs_table = TXT_NewTable(2),
+
+ TXT_NewSeparator("Misc."),
+ run_control = TXT_NewCheckBox("Always run", &always_run),
+ TXT_NewInvertedCheckBox("Use native keyboard mapping",
+ &vanilla_keyboard_mapping),
+ NULL);
+
+ TXT_SetColumnWidths(movement_table, 15, 8, 15, 8);
+
+ TXT_SignalConnect(run_control, "changed", UpdateJoybSpeed, NULL);
+
+ AddKeyControl(movement_table, "Move Forward", &key_up);
+ AddKeyControl(movement_table, " Strafe Left", &key_strafeleft);
+ AddKeyControl(movement_table, "Move Backward", &key_down);
+ AddKeyControl(movement_table, " Strafe Right", &key_straferight);
+ AddKeyControl(movement_table, "Turn Left", &key_left);
+ AddKeyControl(movement_table, " Speed On", &key_speed);
+ AddKeyControl(movement_table, "Turn Right", &key_right);
+ AddKeyControl(movement_table, " Strafe On", &key_strafe);
+
+ if (gamemission == hexen)
+ {
+ AddKeyControl(movement_table, "Jump", &key_jump);
+ }
+
+ TXT_SetColumnWidths(action_table, 15, 8, 15, 8);
+
+ AddKeyControl(action_table, "Fire/Attack", &key_fire);
+ AddKeyControl(action_table, " Use", &key_use);
+
+ // Other key bindings are stored in separate sub-dialogs:
+
+ TXT_SetColumnWidths(dialogs_table, 24, 24);
+
+ TXT_AddWidgets(dialogs_table,
+ TXT_NewButton2("More controls...", ConfigExtraKeys, NULL),
+ TXT_NewButton2("Other keys...", OtherKeysDialog, NULL),
+ NULL);
+
+ TXT_SetWindowAction(window, TXT_HORIZ_CENTER, TestConfigAction());
+
+}
+
+void BindKeyboardVariables(void)
+{
+ M_BindVariable("vanilla_keyboard_mapping", &vanilla_keyboard_mapping);
+}
+
diff --git a/src/setup/keyboard.h b/src/setup/keyboard.h
new file mode 100644
index 00000000..12059bf8
--- /dev/null
+++ b/src/setup/keyboard.h
@@ -0,0 +1,29 @@
+// 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.
+//
+
+#ifndef SETUP_KEYBOARD_H
+#define SETUP_KEYBOARD_H
+
+void ConfigKeyboard(void);
+void BindKeyboardVariables(void);
+
+#endif /* #ifndef SETUP_KEYBOARD_H */
+
diff --git a/src/setup/mainmenu.c b/src/setup/mainmenu.c
new file mode 100644
index 00000000..55496010
--- /dev/null
+++ b/src/setup/mainmenu.c
@@ -0,0 +1,348 @@
+// 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _WIN32_WCE
+#include "libc_wince.h"
+#endif
+
+#include "config.h"
+#include "textscreen.h"
+
+#include "execute.h"
+
+#include "m_argv.h"
+#include "m_config.h"
+
+#include "setup_icon.c"
+#include "mode.h"
+
+#include "compatibility.h"
+#include "display.h"
+#include "joystick.h"
+#include "keyboard.h"
+#include "mouse.h"
+#include "multiplayer.h"
+#include "sound.h"
+
+static const int cheat_sequence[] =
+{
+ KEY_UPARROW, KEY_UPARROW, KEY_DOWNARROW, KEY_DOWNARROW,
+ KEY_LEFTARROW, KEY_RIGHTARROW, KEY_LEFTARROW, KEY_RIGHTARROW,
+ 'b', 'a', KEY_ENTER, 0
+};
+
+static unsigned int cheat_sequence_index = 0;
+
+// I think these are good "sensible" defaults:
+
+static void SensibleDefaults(void)
+{
+#if 0
+ // TODO for raven-branch
+ key_up = 'w';
+ key_down = 's';
+ key_strafeleft = 'a';
+ key_straferight = 'd';
+ mousebprevweapon = 4;
+ mousebnextweapon = 3;
+ snd_musicdevice = 3;
+ joybspeed = 29;
+ vanilla_savegame_limit = 0;
+ vanilla_keyboard_mapping = 0;
+ vanilla_demo_limit = 0;
+ show_endoom = 0;
+ dclick_use = 0;
+ novert = 1;
+#endif
+}
+
+static int MainMenuKeyPress(txt_window_t *window, int key, void *user_data)
+{
+ if (key == cheat_sequence[cheat_sequence_index])
+ {
+ ++cheat_sequence_index;
+
+ if (cheat_sequence[cheat_sequence_index] == 0)
+ {
+ SensibleDefaults();
+ cheat_sequence_index = 0;
+
+ window = TXT_NewWindow(NULL);
+ TXT_AddWidget(window, TXT_NewLabel(" \x01 "));
+ TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, NULL);
+
+ return 1;
+ }
+ }
+ else
+ {
+ cheat_sequence_index = 0;
+ }
+
+ return 0;
+}
+
+static void DoQuit(void *widget, void *dosave)
+{
+ if (dosave != NULL)
+ {
+ M_SaveDefaults();
+ }
+
+ exit(0);
+}
+
+static void QuitConfirm(void *unused1, void *unused2)
+{
+ txt_window_t *window;
+ txt_label_t *label;
+ txt_button_t *yes_button;
+ txt_button_t *no_button;
+
+ window = TXT_NewWindow(NULL);
+
+ TXT_AddWidgets(window,
+ label = TXT_NewLabel("Exiting setup.\nSave settings?"),
+ TXT_NewStrut(24, 0),
+ yes_button = TXT_NewButton2(" Yes ", DoQuit, DoQuit),
+ no_button = TXT_NewButton2(" No ", DoQuit, NULL),
+ NULL);
+
+ TXT_SetWidgetAlign(label, TXT_HORIZ_CENTER);
+ TXT_SetWidgetAlign(yes_button, TXT_HORIZ_CENTER);
+ TXT_SetWidgetAlign(no_button, TXT_HORIZ_CENTER);
+
+ // Only an "abort" button in the middle.
+ TXT_SetWindowAction(window, TXT_HORIZ_LEFT, NULL);
+ TXT_SetWindowAction(window, TXT_HORIZ_CENTER,
+ TXT_NewWindowAbortAction(window));
+ TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, NULL);
+}
+
+static void LaunchDoom(void *unused1, void *unused2)
+{
+ execute_context_t *exec;
+
+ // Save configuration first
+
+ M_SaveDefaults();
+
+ // Shut down textscreen GUI
+
+ TXT_Shutdown();
+
+ // Launch Doom
+
+ exec = NewExecuteContext();
+ PassThroughArguments(exec);
+ ExecuteDoom(exec);
+
+ exit(0);
+}
+
+static txt_button_t *GetLaunchButton(void)
+{
+ char *label;
+
+ switch (gamemission)
+ {
+ case doom:
+ label = "Save parameters and launch DOOM";
+ break;
+ case heretic:
+ label = "Save parameters and launch Heretic";
+ break;
+ case hexen:
+ label = "Save parameters and launch Hexen";
+ break;
+ default:
+ label = "Save parameters and launch game";
+ break;
+ }
+
+ return TXT_NewButton2(label, LaunchDoom, NULL);
+}
+
+void MainMenu(void)
+{
+ txt_window_t *window;
+ txt_window_action_t *quit_action;
+ txt_window_action_t *warp_action;
+
+ window = TXT_NewWindow("Main Menu");
+
+ TXT_AddWidgets(window,
+ TXT_NewButton2("Configure Display",
+ (TxtWidgetSignalFunc) ConfigDisplay, NULL),
+ TXT_NewButton2("Configure Sound",
+ (TxtWidgetSignalFunc) ConfigSound, NULL),
+ TXT_NewButton2("Configure Keyboard",
+ (TxtWidgetSignalFunc) ConfigKeyboard, NULL),
+ TXT_NewButton2("Configure Mouse",
+ (TxtWidgetSignalFunc) ConfigMouse, NULL),
+ TXT_NewButton2("Configure Joystick",
+ (TxtWidgetSignalFunc) ConfigJoystick, NULL),
+ NULL);
+
+ // The compatibility window is only appropriate for Doom.
+
+ if (gamemission == doom)
+ {
+ txt_button_t *button;
+
+ button = TXT_NewButton2("Compatibility",
+ (TxtWidgetSignalFunc) CompatibilitySettings,
+ NULL);
+
+ TXT_AddWidget(window, button);
+ }
+
+ TXT_AddWidgets(window,
+ GetLaunchButton(),
+ TXT_NewStrut(0, 1),
+ TXT_NewButton2("Start a Network Game",
+ (TxtWidgetSignalFunc) StartMultiGame, NULL),
+ TXT_NewButton2("Join a Network Game",
+ (TxtWidgetSignalFunc) JoinMultiGame, NULL),
+ TXT_NewButton2("Multiplayer Configuration",
+ (TxtWidgetSignalFunc) MultiplayerConfig, NULL),
+ NULL);
+
+ quit_action = TXT_NewWindowAction(KEY_ESCAPE, "Quit");
+ warp_action = TXT_NewWindowAction(KEY_F1, "Warp");
+ TXT_SignalConnect(quit_action, "pressed", QuitConfirm, NULL);
+ TXT_SignalConnect(warp_action, "pressed",
+ (TxtWidgetSignalFunc) WarpMenu, NULL);
+ TXT_SetWindowAction(window, TXT_HORIZ_LEFT, quit_action);
+ TXT_SetWindowAction(window, TXT_HORIZ_CENTER, warp_action);
+
+ TXT_SetKeyListener(window, MainMenuKeyPress, NULL);
+}
+
+//
+// Initialize all configuration variables, load config file, etc
+//
+
+static void InitConfig(void)
+{
+ InitBindings();
+
+ SetChatMacroDefaults();
+ SetPlayerNameDefault();
+
+ M_SetConfigDir(NULL);
+ M_LoadDefaults();
+}
+
+//
+// Application icon
+//
+
+static void SetIcon(void)
+{
+ SDL_Surface *surface;
+ Uint8 *mask;
+ int i;
+
+ // Generate the mask
+
+ mask = malloc(setup_icon_w * setup_icon_h / 8);
+ memset(mask, 0, setup_icon_w * setup_icon_h / 8);
+
+ for (i=0; i<setup_icon_w * setup_icon_h; ++i)
+ {
+ if (setup_icon_data[i * 3] != 0x00
+ || setup_icon_data[i * 3 + 1] != 0x00
+ || setup_icon_data[i * 3 + 2] != 0x00)
+ {
+ mask[i / 8] |= 1 << (7 - i % 8);
+ }
+ }
+
+
+ surface = SDL_CreateRGBSurfaceFrom(setup_icon_data,
+ setup_icon_w,
+ setup_icon_h,
+ 24,
+ setup_icon_w * 3,
+ 0xff << 0,
+ 0xff << 8,
+ 0xff << 16,
+ 0);
+
+ SDL_WM_SetIcon(surface, mask);
+ SDL_FreeSurface(surface);
+ free(mask);
+}
+
+// Initialize the textscreen library.
+
+static void InitTextscreen(void)
+{
+ SetDisplayDriver();
+
+ if (!TXT_Init())
+ {
+ fprintf(stderr, "Failed to initialize GUI\n");
+ exit(-1);
+ }
+
+ TXT_SetDesktopTitle(PACKAGE_NAME " Setup ver " PACKAGE_VERSION);
+ SetIcon();
+}
+
+// Restart the textscreen library. Used when the video_driver variable
+// is changed.
+
+void RestartTextscreen(void)
+{
+ TXT_Shutdown();
+ InitTextscreen();
+}
+
+//
+// Initialize and run the textscreen GUI.
+//
+
+static void RunGUI(void)
+{
+ InitTextscreen();
+
+ TXT_GUIMainLoop();
+}
+
+static void MissionSet(void)
+{
+ InitConfig();
+ MainMenu();
+}
+
+void D_DoomMain(void)
+{
+ SetupMission(MissionSet);
+
+ RunGUI();
+}
+
diff --git a/src/setup/mode.c b/src/setup/mode.c
new file mode 100644
index 00000000..702db67d
--- /dev/null
+++ b/src/setup/mode.c
@@ -0,0 +1,339 @@
+// 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>
+
+#include "doomtype.h"
+
+#include "config.h"
+#include "textscreen.h"
+
+#include "doomtype.h"
+#include "d_mode.h"
+#include "d_iwad.h"
+#include "i_system.h"
+#include "m_argv.h"
+#include "m_config.h"
+#include "m_controls.h"
+
+#include "compatibility.h"
+#include "display.h"
+#include "joystick.h"
+#include "keyboard.h"
+#include "mouse.h"
+#include "multiplayer.h"
+#include "sound.h"
+
+#include "mode.h"
+
+GameMission_t gamemission;
+static iwad_t **iwads;
+
+typedef struct
+{
+ char *label;
+ GameMission_t mission;
+ int mask;
+ char *name;
+ char *config_file;
+ char *extra_config_file;
+ char *executable;
+} mission_config_t;
+
+// Default mission to fall back on, if no IWADs are found at all:
+
+#define DEFAULT_MISSION (&mission_configs[0])
+
+static mission_config_t mission_configs[] =
+{
+ {
+ "Doom",
+ doom,
+ IWAD_MASK_DOOM,
+ "doom",
+ "default.cfg",
+ PROGRAM_PREFIX "doom.cfg",
+ PROGRAM_PREFIX "doom"
+ },
+ {
+ "Heretic",
+ heretic,
+ IWAD_MASK_HERETIC,
+ "heretic",
+ "heretic.cfg",
+ PROGRAM_PREFIX "heretic.cfg",
+ PROGRAM_PREFIX "heretic"
+ },
+ {
+ "Hexen",
+ hexen,
+ IWAD_MASK_HEXEN,
+ "hexen",
+ "hexen.cfg",
+ PROGRAM_PREFIX "hexen.cfg",
+ PROGRAM_PREFIX "hexen"
+ }
+};
+
+static GameSelectCallback game_selected_callback;
+
+// Miscellaneous variables that aren't used in setup.
+
+static int showMessages = 1;
+static int screenblocks = 9;
+static int detailLevel = 0;
+static char *savedir = NULL;
+static char *executable = NULL;
+
+static void BindMiscVariables(void)
+{
+ M_BindVariable("screenblocks", &screenblocks);
+
+ if (gamemission == doom)
+ {
+ M_BindVariable("detaillevel", &detailLevel);
+ M_BindVariable("show_messages", &showMessages);
+ }
+
+ if (gamemission == hexen)
+ {
+ M_BindVariable("savedir", &savedir);
+ M_BindVariable("messageson", &showMessages);
+ }
+}
+
+//
+// Initialise all configuration file bindings.
+//
+
+void InitBindings(void)
+{
+ M_ApplyPlatformDefaults();
+
+ // Keyboard, mouse, joystick controls
+
+ M_BindBaseControls();
+ M_BindWeaponControls();
+ M_BindMapControls();
+ M_BindMenuControls();
+
+ if (gamemission == heretic || gamemission == hexen)
+ {
+ M_BindHereticControls();
+ }
+
+ if (gamemission == hexen)
+ {
+ M_BindHexenControls();
+ }
+
+ // All other variables
+
+ BindCompatibilityVariables();
+ BindDisplayVariables();
+ BindJoystickVariables();
+ BindKeyboardVariables();
+ BindMouseVariables();
+ BindSoundVariables();
+ BindMiscVariables();
+ BindMultiplayerVariables();
+}
+
+// Set the name of the executable program to run the game:
+
+static void SetExecutable(mission_config_t *config)
+{
+ char *extension;
+
+ free(executable);
+
+#ifdef _WIN32
+ extension = ".exe";
+#else
+ extension = "";
+#endif
+
+ executable = malloc(strlen(config->executable) + 5);
+ sprintf(executable, "%s%s", config->executable, extension);
+}
+
+static void SetMission(mission_config_t *config)
+{
+ iwads = D_FindAllIWADs(config->mask);
+ gamemission = config->mission;
+ SetExecutable(config);
+ M_SetConfigFilenames(config->config_file, config->extra_config_file);
+}
+
+static mission_config_t *GetMissionForName(char *name)
+{
+ int i;
+
+ for (i=0; i<arrlen(mission_configs); ++i)
+ {
+ if (!strcmp(mission_configs[i].name, name))
+ {
+ return &mission_configs[i];
+ }
+ }
+
+ return NULL;
+}
+
+// Check the name of the executable. If it contains one of the game
+// names (eg. chocolate-hexen-setup.exe) then use that game.
+
+static boolean CheckExecutableName(GameSelectCallback callback)
+{
+ mission_config_t *config;
+ char *exe_name;
+ int i;
+
+ exe_name = M_GetExecutableName();
+
+ for (i=0; i<arrlen(mission_configs); ++i)
+ {
+ config = &mission_configs[i];
+
+ if (strstr(exe_name, config->name) != NULL)
+ {
+ SetMission(config);
+ callback();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void GameSelected(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(config))
+{
+ TXT_CAST_ARG(mission_config_t, config);
+
+ SetMission(config);
+ game_selected_callback();
+}
+
+static void OpenGameSelectDialog(GameSelectCallback callback)
+{
+ mission_config_t *mission = NULL;
+ txt_window_t *window;
+ iwad_t **iwads;
+ int num_games;
+ int i;
+
+ window = TXT_NewWindow("Select game");
+
+ TXT_AddWidget(window, TXT_NewLabel("Select a game to configure:\n"));
+ num_games = 0;
+
+ // Add a button for each game.
+
+ for (i=0; i<arrlen(mission_configs); ++i)
+ {
+ // Do we have any IWADs for this game installed?
+ // If so, add a button.
+
+ iwads = D_FindAllIWADs(mission_configs[i].mask);
+
+ if (iwads[0] != NULL)
+ {
+ mission = &mission_configs[i];
+ TXT_AddWidget(window, TXT_NewButton2(mission_configs[i].label,
+ GameSelected,
+ &mission_configs[i]));
+ ++num_games;
+ }
+
+ free(iwads);
+ }
+
+ TXT_AddWidget(window, TXT_NewStrut(0, 1));
+
+ // No IWADs found at all? Fall back to doom, then.
+
+ if (num_games == 0)
+ {
+ TXT_CloseWindow(window);
+ SetMission(DEFAULT_MISSION);
+ callback();
+ return;
+ }
+
+ // Only one game? Use that game, and don't bother with a dialog.
+
+ if (num_games == 1)
+ {
+ TXT_CloseWindow(window);
+ SetMission(mission);
+ callback();
+ return;
+ }
+
+ game_selected_callback = callback;
+}
+
+void SetupMission(GameSelectCallback callback)
+{
+ mission_config_t *config;
+ char *mission_name;
+ int p;
+
+ //!
+ // @arg <game>
+ //
+ // Specify the game to configure the settings for. Valid
+ // values are 'doom', 'heretic' and 'hexen'.
+ //
+
+ p = M_CheckParm("-game");
+
+ if (p > 0)
+ {
+ mission_name = myargv[p + 1];
+
+ config = GetMissionForName(mission_name);
+
+ if (config == NULL)
+ {
+ I_Error("Invalid parameter - '%s'", mission_name);
+ }
+
+ SetMission(config);
+ callback();
+ }
+ else if (!CheckExecutableName(callback))
+ {
+ OpenGameSelectDialog(callback);
+ }
+}
+
+char *GetExecutableName(void)
+{
+ return executable;
+}
+
+iwad_t **GetIwads(void)
+{
+ return iwads;
+}
+
diff --git a/src/setup/mode.h b/src/setup/mode.h
new file mode 100644
index 00000000..44046c38
--- /dev/null
+++ b/src/setup/mode.h
@@ -0,0 +1,37 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2008 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.
+//
+
+#ifndef SETUP_MODE_H
+#define SETUP_MODE_H
+
+#include "d_mode.h"
+#include "d_iwad.h"
+
+typedef void (*GameSelectCallback)(void);
+extern GameMission_t gamemission;
+
+void SetupMission(GameSelectCallback callback);
+void InitBindings(void);
+char *GetExecutableName(void);
+iwad_t **GetIwads(void);
+
+#endif /* #ifndef SETUP_MODE_H */
+
diff --git a/src/setup/mouse.c b/src/setup/mouse.c
new file mode 100644
index 00000000..5b555f88
--- /dev/null
+++ b/src/setup/mouse.c
@@ -0,0 +1,169 @@
+// 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 "textscreen.h"
+#include "doomtype.h"
+#include "m_config.h"
+#include "m_controls.h"
+
+#include "execute.h"
+#include "txt_mouseinput.h"
+
+#include "mode.h"
+#include "mouse.h"
+
+static int usemouse = 1;
+
+static int novert = 0;
+static int mouseSensitivity = 5;
+static float mouse_acceleration = 2.0;
+static int mouse_threshold = 10;
+static int grabmouse = 1;
+
+static int *all_mouse_buttons[] = {
+ &mousebfire,
+ &mousebstrafe,
+ &mousebforward,
+ &mousebstrafeleft,
+ &mousebstraferight,
+ &mousebbackward,
+ &mousebuse,
+ &mousebjump,
+ &mousebprevweapon,
+ &mousebnextweapon
+};
+
+static void MouseSetCallback(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(variable))
+{
+ TXT_CAST_ARG(int, variable);
+ unsigned int i;
+
+ // Check if the same mouse button is used for a different action
+ // If so, set the other action(s) to -1 (unset)
+
+ for (i=0; i<arrlen(all_mouse_buttons); ++i)
+ {
+ if (*all_mouse_buttons[i] == *variable
+ && all_mouse_buttons[i] != variable)
+ {
+ *all_mouse_buttons[i] = -1;
+ }
+ }
+}
+
+static void AddMouseControl(txt_table_t *table, char *label, int *var)
+{
+ txt_mouse_input_t *mouse_input;
+
+ TXT_AddWidget(table, TXT_NewLabel(label));
+
+ mouse_input = TXT_NewMouseInput(var);
+ TXT_AddWidget(table, mouse_input);
+
+ TXT_SignalConnect(mouse_input, "set", MouseSetCallback, var);
+}
+
+static void ConfigExtraButtons(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
+{
+ txt_window_t *window;
+ txt_table_t *buttons_table;
+
+ window = TXT_NewWindow("Additional mouse buttons");
+
+ TXT_AddWidgets(window,
+ buttons_table = TXT_NewTable(2),
+ NULL);
+
+ TXT_SetColumnWidths(buttons_table, 29, 5);
+
+ AddMouseControl(buttons_table, "Move backward", &mousebbackward);
+ AddMouseControl(buttons_table, "Use", &mousebuse);
+ AddMouseControl(buttons_table, "Strafe left", &mousebstrafeleft);
+ AddMouseControl(buttons_table, "Strafe right", &mousebstraferight);
+
+ if (gamemission == hexen)
+ {
+ AddMouseControl(buttons_table, "Jump", &mousebjump);
+ }
+
+ AddMouseControl(buttons_table, "Previous weapon", &mousebprevweapon);
+ AddMouseControl(buttons_table, "Next weapon", &mousebnextweapon);
+}
+
+void ConfigMouse(void)
+{
+ txt_window_t *window;
+ txt_table_t *motion_table;
+ txt_table_t *buttons_table;
+
+ window = TXT_NewWindow("Mouse configuration");
+
+ TXT_AddWidgets(window,
+ TXT_NewCheckBox("Enable mouse", &usemouse),
+ TXT_NewInvertedCheckBox("Allow vertical mouse movement",
+ &novert),
+ TXT_NewCheckBox("Grab mouse in windowed mode",
+ &grabmouse),
+ TXT_NewCheckBox("Double click acts as \"use\"",
+ &dclick_use),
+
+ TXT_NewSeparator("Mouse motion"),
+ motion_table = TXT_NewTable(2),
+
+ TXT_NewSeparator("Buttons"),
+ buttons_table = TXT_NewTable(2),
+ TXT_NewButton2("More controls...",
+ ConfigExtraButtons,
+ NULL),
+ NULL);
+
+ TXT_SetColumnWidths(motion_table, 27, 5);
+
+ TXT_AddWidgets(motion_table,
+ TXT_NewLabel("Speed"),
+ TXT_NewSpinControl(&mouseSensitivity, 1, 256),
+ TXT_NewLabel("Acceleration"),
+ TXT_NewFloatSpinControl(&mouse_acceleration, 1.0, 5.0),
+ TXT_NewLabel("Acceleration threshold"),
+ TXT_NewSpinControl(&mouse_threshold, 0, 32),
+ NULL);
+
+ TXT_SetColumnWidths(buttons_table, 27, 5);
+
+ AddMouseControl(buttons_table, "Fire/Attack", &mousebfire);
+ AddMouseControl(buttons_table, "Move forward", &mousebforward);
+ AddMouseControl(buttons_table, "Strafe on", &mousebstrafe);
+
+ TXT_SetWindowAction(window, TXT_HORIZ_CENTER, TestConfigAction());
+}
+
+void BindMouseVariables(void)
+{
+ M_BindVariable("use_mouse", &usemouse);
+ M_BindVariable("novert", &novert);
+ M_BindVariable("mouse_sensitivity", &mouseSensitivity);
+ M_BindVariable("mouse_acceleration", &mouse_acceleration);
+ M_BindVariable("mouse_threshold", &mouse_threshold);
+ M_BindVariable("grabmouse", &grabmouse);
+}
+
diff --git a/src/setup/mouse.h b/src/setup/mouse.h
new file mode 100644
index 00000000..cdaa541a
--- /dev/null
+++ b/src/setup/mouse.h
@@ -0,0 +1,29 @@
+// 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.
+//
+
+#ifndef SETUP_MOUSE_H
+#define SETUP_MOUSE_H
+
+void ConfigMouse(void);
+void BindMouseVariables(void);
+
+#endif /* #ifndef SETUP_MOUSE_H */
+
diff --git a/src/setup/multiplayer.c b/src/setup/multiplayer.c
new file mode 100644
index 00000000..aed89212
--- /dev/null
+++ b/src/setup/multiplayer.c
@@ -0,0 +1,968 @@
+// 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "doomtype.h"
+#include "textscreen.h"
+
+#include "d_iwad.h"
+#include "m_config.h"
+#include "doom/d_englsh.h"
+#include "m_controls.h"
+
+#include "multiplayer.h"
+#include "mode.h"
+#include "execute.h"
+
+#define NUM_WADS 10
+#define NUM_EXTRA_PARAMS 10
+
+typedef enum
+{
+ WARP_ExMy,
+ WARP_MAPxy,
+} warptype_t;
+
+typedef enum
+{
+ JOIN_AUTO_LAN,
+ JOIN_ADDRESS,
+} jointype_t;
+
+// Fallback IWAD if none are found to be installed
+
+static iwad_t fallback_iwad = { "doom2.wad", doom2, commercial, "Doom II" };
+static iwad_t *fallback_iwad_list[2] = { &fallback_iwad, NULL };
+
+// Array of IWADs found to be installed
+
+static iwad_t **found_iwads;
+static char *iwad_labels[8];
+
+// Index of the currently selected IWAD
+
+static int found_iwad_selected;
+
+// Filename to pass to '-iwad'.
+
+static char *iwadfile;
+
+static char *doom_skills[] =
+{
+ "I'm too young to die.",
+ "Hey, not too rough.",
+ "Hurt me plenty.",
+ "Ultra-Violence.",
+ "NIGHTMARE!",
+};
+
+static char *chex_skills[] =
+{
+ "Easy does it",
+ "Not so sticky",
+ "Gobs of goo",
+ "Extreme ooze",
+ "SUPER SLIMEY!"
+};
+
+static char *heretic_skills[] =
+{
+ "Thou needeth a wet-nurse",
+ "Yellowbellies-R-us",
+ "Bringest them oneth",
+ "Thou art a smite-meister",
+ "Black plague possesses thee"
+};
+
+static char *hexen_skills[] =
+{
+ "Squire/Altar boy/Apprentice",
+ "Knight/Acolyte/Enchanter",
+ "Warrior/Priest/Sorceror",
+ "Berserker/Cardinal/Warlock",
+ "Titan/Pope/Archimage"
+};
+
+static char *character_classes[] =
+{
+ "Fighter",
+ "Cleric",
+ "Mage"
+};
+
+static struct
+{
+ GameMission_t mission;
+ char **strings;
+} skills[] =
+{
+ { doom, doom_skills },
+ { heretic, heretic_skills },
+ { hexen, hexen_skills }
+};
+
+static char *gamemodes[] =
+{
+ "Co-operative",
+ "Deathmatch",
+ "Deathmatch 2.0",
+};
+
+static char *net_player_name;
+static char *chat_macros[10];
+
+static int jointype = JOIN_ADDRESS;
+
+static char *wads[NUM_WADS];
+static char *extra_params[NUM_EXTRA_PARAMS];
+static int character_class = 0;
+static int skill = 2;
+static int nomonsters = 0;
+static int deathmatch = 0;
+static int fast = 0;
+static int respawn = 0;
+static int udpport = 2342;
+static int timer = 0;
+static int privateserver = 0;
+
+static txt_dropdown_list_t *skillbutton;
+static txt_button_t *warpbutton;
+static warptype_t warptype = WARP_MAPxy;
+static int warpepisode = 1;
+static int warpmap = 1;
+
+// Address to connect to when joining a game
+
+static char *connect_address = NULL;
+
+// Find an IWAD from its description
+
+static iwad_t *GetCurrentIWAD(void)
+{
+ return found_iwads[found_iwad_selected];
+}
+
+// Is the currently selected IWAD the Chex Quest chex.wad?
+
+static boolean IsChexQuest(iwad_t *iwad)
+{
+ return !strcmp(iwad->name, "chex.wad");
+}
+
+static void AddWADs(execute_context_t *exec)
+{
+ int have_wads = 0;
+ int i;
+
+ for (i=0; i<NUM_WADS; ++i)
+ {
+ if (wads[i] != NULL && strlen(wads[i]) > 0)
+ {
+ if (!have_wads)
+ {
+ AddCmdLineParameter(exec, "-file");
+ }
+
+ AddCmdLineParameter(exec, "\"%s\"", wads[i]);
+ }
+ }
+}
+
+static void AddExtraParameters(execute_context_t *exec)
+{
+ int i;
+
+ for (i=0; i<NUM_EXTRA_PARAMS; ++i)
+ {
+ if (extra_params[i] != NULL && strlen(extra_params[i]) > 0)
+ {
+ AddCmdLineParameter(exec, extra_params[i]);
+ }
+ }
+}
+
+static void AddIWADParameter(execute_context_t *exec)
+{
+ if (iwadfile != NULL)
+ {
+ AddCmdLineParameter(exec, "-iwad %s", iwadfile);
+ }
+}
+
+// Callback function invoked to launch the game.
+// This is used when starting a server and also when starting a
+// single player game via the "warp" menu.
+
+static void StartGame(int multiplayer)
+{
+ execute_context_t *exec;
+
+ exec = NewExecuteContext();
+
+ // Extra parameters come first, before all others; this way,
+ // they can override any of the options set in the dialog.
+
+ AddExtraParameters(exec);
+
+ AddIWADParameter(exec);
+ AddCmdLineParameter(exec, "-skill %i", skill + 1);
+
+ if (gamemission == hexen)
+ {
+ AddCmdLineParameter(exec, "-class %i", character_class);
+ }
+
+ if (nomonsters)
+ {
+ AddCmdLineParameter(exec, "-nomonsters");
+ }
+
+ if (fast)
+ {
+ AddCmdLineParameter(exec, "-fast");
+ }
+
+ if (respawn)
+ {
+ AddCmdLineParameter(exec, "-respawn");
+ }
+
+ if (warptype == WARP_ExMy)
+ {
+ // TODO: select IWAD based on warp type
+ AddCmdLineParameter(exec, "-warp %i %i", warpepisode, warpmap);
+ }
+ else if (warptype == WARP_MAPxy)
+ {
+ AddCmdLineParameter(exec, "-warp %i", warpmap);
+ }
+
+ // Multiplayer-specific options:
+
+ if (multiplayer)
+ {
+ AddCmdLineParameter(exec, "-server");
+ AddCmdLineParameter(exec, "-port %i", udpport);
+
+ if (deathmatch == 1)
+ {
+ AddCmdLineParameter(exec, "-deathmatch");
+ }
+ else if (deathmatch == 2)
+ {
+ AddCmdLineParameter(exec, "-altdeath");
+ }
+
+ if (timer > 0)
+ {
+ AddCmdLineParameter(exec, "-timer %i", timer);
+ }
+
+ if (privateserver)
+ {
+ AddCmdLineParameter(exec, "-privateserver");
+ }
+ }
+
+ AddWADs(exec);
+
+ TXT_Shutdown();
+
+ M_SaveDefaults();
+ PassThroughArguments(exec);
+
+ ExecuteDoom(exec);
+
+ exit(0);
+}
+
+static void StartServerGame(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
+{
+ StartGame(1);
+}
+
+static void StartSinglePlayerGame(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
+{
+ StartGame(0);
+}
+
+static void UpdateWarpButton(void)
+{
+ char buf[10];
+
+ if (warptype == WARP_ExMy)
+ {
+ sprintf(buf, "E%iM%i", warpepisode, warpmap);
+ }
+ else if (warptype == WARP_MAPxy)
+ {
+ sprintf(buf, "MAP%02i", warpmap);
+ }
+
+ TXT_SetButtonLabel(warpbutton, buf);
+}
+
+static void UpdateSkillButton(void)
+{
+ iwad_t *iwad = GetCurrentIWAD();
+ int i;
+
+ if (IsChexQuest(iwad))
+ {
+ skillbutton->values = chex_skills;
+ }
+ else
+ {
+ for (i=0; i<arrlen(skills); ++i)
+ {
+ if (gamemission == skills[i].mission)
+ {
+ skillbutton->values = skills[i].strings;
+ break;
+ }
+ }
+ }
+}
+
+static void SetExMyWarp(TXT_UNCAST_ARG(widget), void *val)
+{
+ int l;
+
+ l = (int) val;
+
+ warpepisode = l / 10;
+ warpmap = l % 10;
+
+ UpdateWarpButton();
+}
+
+static void SetMAPxyWarp(TXT_UNCAST_ARG(widget), void *val)
+{
+ int l;
+
+ l = (int) val;
+
+ warpmap = l;
+
+ UpdateWarpButton();
+}
+
+static void CloseLevelSelectDialog(TXT_UNCAST_ARG(button), TXT_UNCAST_ARG(window))
+{
+ TXT_CAST_ARG(txt_window_t, window);
+
+ TXT_CloseWindow(window);
+}
+
+static void LevelSelectDialog(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(user_data))
+{
+ txt_window_t *window;
+ txt_table_t *table;
+ txt_button_t *button;
+ iwad_t *iwad;
+ char buf[10];
+ int episodes;
+ int x, y;
+ int l;
+ int i;
+
+ window = TXT_NewWindow("Select level");
+ iwad = GetCurrentIWAD();
+
+ if (warptype == WARP_ExMy)
+ {
+ episodes = D_GetNumEpisodes(iwad->mission, iwad->mode);
+ table = TXT_NewTable(episodes);
+
+ // ExMy levels
+
+ for (y=1; y<10; ++y)
+ {
+ for (x=1; x<=episodes; ++x)
+ {
+ if (IsChexQuest(iwad) && (x > 1 || y > 5))
+ {
+ continue;
+ }
+
+ if (!D_ValidEpisodeMap(iwad->mission, iwad->mode, x, y))
+ {
+ TXT_AddWidget(table, NULL);
+ continue;
+ }
+
+ sprintf(buf, " E%iM%i ", x, y);
+ button = TXT_NewButton(buf);
+ TXT_SignalConnect(button, "pressed",
+ SetExMyWarp, (void *) (x * 10 + y));
+ TXT_SignalConnect(button, "pressed",
+ CloseLevelSelectDialog, window);
+ TXT_AddWidget(table, button);
+
+ if (warpepisode == x && warpmap == y)
+ {
+ TXT_SelectWidget(table, button);
+ }
+ }
+ }
+ }
+ else
+ {
+ table = TXT_NewTable(4);
+
+ for (i=0; i<40; ++i)
+ {
+ x = i % 4;
+ y = i / 4;
+
+ l = x * 10 + y + 1;
+
+ if (!D_ValidEpisodeMap(iwad->mission, iwad->mode, 1, l))
+ {
+ TXT_AddWidget(table, NULL);
+ continue;
+ }
+
+ sprintf(buf, " MAP%02i ", l);
+ button = TXT_NewButton(buf);
+ TXT_SignalConnect(button, "pressed",
+ SetMAPxyWarp, (void *) l);
+ TXT_SignalConnect(button, "pressed",
+ CloseLevelSelectDialog, window);
+ TXT_AddWidget(table, button);
+
+ if (warpmap == l)
+ {
+ TXT_SelectWidget(table, button);
+ }
+ }
+ }
+
+ TXT_AddWidget(window, table);
+}
+
+static void IWADSelected(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
+{
+ iwad_t *iwad;
+
+ // Find the iwad_t selected
+
+ iwad = GetCurrentIWAD();
+
+ // Update iwadfile
+
+ iwadfile = iwad->name;
+}
+
+// Called when the IWAD button is changed, to update warptype.
+
+static void UpdateWarpType(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
+{
+ warptype_t new_warptype;
+ iwad_t *iwad;
+
+ // Get the selected IWAD
+
+ iwad = GetCurrentIWAD();
+
+ // Find the new warp type
+
+ if (D_IsEpisodeMap(iwad->mission))
+ {
+ new_warptype = WARP_ExMy;
+ }
+ else
+ {
+ new_warptype = WARP_MAPxy;
+ }
+
+ // Reset to E1M1 / MAP01 when the warp type is changed.
+
+ if (new_warptype != warptype)
+ {
+ warpepisode = 1;
+ warpmap = 1;
+ }
+
+ warptype = new_warptype;
+
+ UpdateWarpButton();
+ UpdateSkillButton();
+}
+
+static txt_widget_t *IWADSelector(void)
+{
+ txt_dropdown_list_t *dropdown;
+ txt_widget_t *result;
+ int num_iwads;
+ unsigned int i;
+
+ // Find out what WADs are installed
+
+ found_iwads = GetIwads();
+
+ // Build a list of the descriptions for all installed IWADs
+
+ num_iwads = 0;
+
+ for (i=0; found_iwads[i] != NULL; ++i)
+ {
+ iwad_labels[i] = found_iwads[i]->description;
+ ++num_iwads;
+ }
+
+ // If no IWADs are found, provide Doom 2 as an option, but
+ // we're probably screwed.
+
+ if (num_iwads == 0)
+ {
+ found_iwads = fallback_iwad_list;
+ num_iwads = 1;
+ }
+
+ // Build a dropdown list of IWADs
+
+ if (num_iwads < 2)
+ {
+ // We have only one IWAD. Show as a label.
+
+ result = (txt_widget_t *) TXT_NewLabel(found_iwads[0]->description);
+ }
+ else
+ {
+ // Dropdown list allowing IWAD to be selected.
+
+ dropdown = TXT_NewDropdownList(&found_iwad_selected,
+ iwad_labels, num_iwads);
+
+ TXT_SignalConnect(dropdown, "changed", IWADSelected, NULL);
+
+ result = (txt_widget_t *) dropdown;
+ }
+
+ // Select first in the list.
+
+ found_iwad_selected = 0;
+ IWADSelected(NULL, NULL);
+
+ return result;
+}
+
+// Create the window action button to start the game. This invokes
+// a different callback depending on whether to start a multiplayer
+// or single player game.
+
+static txt_window_action_t *StartGameAction(int multiplayer)
+{
+ txt_window_action_t *action;
+ TxtWidgetSignalFunc callback;
+
+ action = TXT_NewWindowAction(KEY_F10, "Start");
+
+ if (multiplayer)
+ {
+ callback = StartServerGame;
+ }
+ else
+ {
+ callback = StartSinglePlayerGame;
+ }
+
+ TXT_SignalConnect(action, "pressed", callback, NULL);
+
+ return action;
+}
+
+static void OpenWadsWindow(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(user_data))
+{
+ txt_window_t *window;
+ int i;
+
+ window = TXT_NewWindow("Add WADs");
+
+ for (i=0; i<NUM_WADS; ++i)
+ {
+ TXT_AddWidget(window, TXT_NewInputBox(&wads[i], 60));
+ }
+}
+
+static void OpenExtraParamsWindow(TXT_UNCAST_ARG(widget),
+ TXT_UNCAST_ARG(user_data))
+{
+ txt_window_t *window;
+ int i;
+
+ window = TXT_NewWindow("Extra command line parameters");
+
+ for (i=0; i<NUM_EXTRA_PARAMS; ++i)
+ {
+ TXT_AddWidget(window, TXT_NewInputBox(&extra_params[i], 70));
+ }
+}
+
+static txt_window_action_t *WadWindowAction(void)
+{
+ txt_window_action_t *action;
+
+ action = TXT_NewWindowAction('w', "Add WADs");
+ TXT_SignalConnect(action, "pressed", OpenWadsWindow, NULL);
+
+ return action;
+}
+
+// "Start game" menu. This is used for the start server window
+// and the single player warp menu. The parameters specify
+// the window title and whether to display multiplayer options.
+
+static void StartGameMenu(char *window_title, int multiplayer)
+{
+ txt_window_t *window;
+ txt_table_t *gameopt_table;
+ txt_table_t *advanced_table;
+ txt_widget_t *iwad_selector;
+ int num_mult_types = 2;
+
+ window = TXT_NewWindow(window_title);
+
+ TXT_AddWidgets(window,
+ gameopt_table = TXT_NewTable(2),
+ TXT_NewSeparator("Monster options"),
+ TXT_NewInvertedCheckBox("Monsters enabled", &nomonsters),
+ TXT_NewCheckBox("Fast monsters", &fast),
+ TXT_NewCheckBox("Respawning monsters", &respawn),
+ TXT_NewSeparator("Advanced"),
+ advanced_table = TXT_NewTable(2),
+ NULL);
+
+ TXT_SetWindowAction(window, TXT_HORIZ_CENTER, WadWindowAction());
+ TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, StartGameAction(multiplayer));
+
+ TXT_SetColumnWidths(gameopt_table, 12, 6);
+
+ if (gamemission == doom)
+ {
+ num_mult_types = 3;
+ }
+ else
+ {
+ num_mult_types = 2;
+ }
+
+ TXT_AddWidgets(gameopt_table,
+ TXT_NewLabel("Game"),
+ iwad_selector = IWADSelector(),
+ TXT_NewLabel("Skill"),
+ skillbutton = TXT_NewDropdownList(&skill, doom_skills, 5),
+ TXT_NewLabel("Level warp"),
+ warpbutton = TXT_NewButton2("????", LevelSelectDialog, NULL),
+ NULL);
+
+ if (gamemission == hexen)
+ {
+ TXT_AddWidgets(gameopt_table,
+ TXT_NewLabel("Character class "),
+ TXT_NewDropdownList(&character_class,
+ character_classes, 3),
+ NULL);
+ }
+
+ if (multiplayer)
+ {
+ TXT_AddWidgets(gameopt_table,
+ TXT_NewLabel("Game type"),
+ TXT_NewDropdownList(&deathmatch, gamemodes, num_mult_types),
+ TXT_NewLabel("Time limit"),
+ TXT_NewHorizBox(TXT_NewIntInputBox(&timer, 2),
+ TXT_NewLabel("minutes"),
+ NULL),
+ NULL);
+
+ TXT_AddWidget(window,
+ TXT_NewInvertedCheckBox("Register with master server",
+ &privateserver));
+
+ TXT_AddWidgets(advanced_table,
+ TXT_NewLabel("UDP port"),
+ TXT_NewIntInputBox(&udpport, 5),
+ NULL);
+ }
+
+ TXT_AddWidget(window,
+ TXT_NewButton2("Add extra parameters...",
+ OpenExtraParamsWindow, NULL));
+
+ TXT_SetColumnWidths(advanced_table, 12, 6);
+
+ TXT_SignalConnect(iwad_selector, "changed", UpdateWarpType, NULL);
+
+ UpdateWarpType(NULL, NULL);
+ UpdateWarpButton();
+}
+
+void StartMultiGame(void)
+{
+ StartGameMenu("Start multiplayer game", 1);
+}
+
+void WarpMenu(void)
+{
+ StartGameMenu("Level Warp", 0);
+}
+
+static void DoJoinGame(void *unused1, void *unused2)
+{
+ execute_context_t *exec;
+
+ exec = NewExecuteContext();
+
+ if (jointype == JOIN_ADDRESS)
+ {
+ AddCmdLineParameter(exec, "-connect %s", connect_address);
+ }
+ else if (jointype == JOIN_AUTO_LAN)
+ {
+ AddCmdLineParameter(exec, "-autojoin");
+ }
+
+ if (gamemission == hexen)
+ {
+ AddCmdLineParameter(exec, "-class %i", character_class);
+ }
+
+ // Extra parameters come first, so that they can be used to override
+ // the other parameters.
+
+ AddExtraParameters(exec);
+ AddIWADParameter(exec);
+ AddWADs(exec);
+
+ TXT_Shutdown();
+
+ M_SaveDefaults();
+
+ PassThroughArguments(exec);
+
+ ExecuteDoom(exec);
+
+ exit(0);
+}
+
+static txt_window_action_t *JoinGameAction(void)
+{
+ txt_window_action_t *action;
+
+ action = TXT_NewWindowAction(KEY_F10, "Connect");
+ TXT_SignalConnect(action, "pressed", DoJoinGame, NULL);
+
+ return action;
+}
+
+// When an address is entered, select "address" mode.
+
+static void SelectAddressJoin(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
+{
+ jointype = JOIN_ADDRESS;
+}
+
+void JoinMultiGame(void)
+{
+ txt_window_t *window;
+ txt_table_t *gameopt_table;
+ txt_table_t *serveropt_table;
+ txt_inputbox_t *address_box;
+
+ window = TXT_NewWindow("Join multiplayer game");
+
+ TXT_AddWidgets(window,
+ gameopt_table = TXT_NewTable(2),
+ TXT_NewSeparator("Server"),
+ serveropt_table = TXT_NewTable(2),
+ TXT_NewStrut(0, 1),
+ TXT_NewButton2("Add extra parameters...", OpenExtraParamsWindow, NULL),
+ NULL);
+
+ TXT_SetColumnWidths(gameopt_table, 12, 12);
+
+ TXT_AddWidgets(gameopt_table,
+ TXT_NewLabel("Game"),
+ IWADSelector(),
+ NULL);
+
+ if (gamemission == hexen)
+ {
+ TXT_AddWidgets(gameopt_table,
+ TXT_NewLabel("Character class "),
+ TXT_NewDropdownList(&character_class,
+ character_classes, 3),
+ NULL);
+ }
+
+ TXT_AddWidgets(serveropt_table,
+ TXT_NewRadioButton("Connect to address:",
+ &jointype, JOIN_ADDRESS),
+ address_box = TXT_NewInputBox(&connect_address, 30),
+ TXT_NewRadioButton("Auto-join LAN game",
+ &jointype, JOIN_AUTO_LAN),
+ NULL);
+
+ TXT_SignalConnect(address_box, "changed", SelectAddressJoin, NULL);
+ TXT_SelectWidget(window, address_box);
+
+ TXT_SetWindowAction(window, TXT_HORIZ_CENTER, WadWindowAction());
+ TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, JoinGameAction());
+}
+
+void SetChatMacroDefaults(void)
+{
+ int i;
+ char *defaults[] =
+ {
+ HUSTR_CHATMACRO1,
+ HUSTR_CHATMACRO2,
+ HUSTR_CHATMACRO3,
+ HUSTR_CHATMACRO4,
+ HUSTR_CHATMACRO5,
+ HUSTR_CHATMACRO6,
+ HUSTR_CHATMACRO7,
+ HUSTR_CHATMACRO8,
+ HUSTR_CHATMACRO9,
+ HUSTR_CHATMACRO0,
+ };
+
+ // If the chat macros have not been set, initialize with defaults.
+
+ for (i=0; i<10; ++i)
+ {
+ if (chat_macros[i] == NULL)
+ {
+ chat_macros[i] = strdup(defaults[i]);
+ }
+ }
+}
+
+void SetPlayerNameDefault(void)
+{
+ if (net_player_name == NULL)
+ {
+ net_player_name = getenv("USER");
+ }
+
+ if (net_player_name == NULL)
+ {
+ net_player_name = getenv("USERNAME");
+ }
+
+ if (net_player_name == NULL)
+ {
+ net_player_name = "player";
+ }
+}
+
+void MultiplayerConfig(void)
+{
+ txt_window_t *window;
+ txt_label_t *label;
+ txt_table_t *table;
+ char buf[10];
+ int i;
+
+ window = TXT_NewWindow("Multiplayer Configuration");
+
+ TXT_AddWidgets(window,
+ TXT_NewStrut(0, 1),
+ TXT_NewHorizBox(TXT_NewLabel("Player name: "),
+ TXT_NewInputBox(&net_player_name, 25),
+ NULL),
+ TXT_NewStrut(0, 1),
+ TXT_NewSeparator("Chat macros"),
+ NULL);
+
+ table = TXT_NewTable(2);
+
+ for (i=0; i<10; ++i)
+ {
+ sprintf(buf, "#%i ", i + 1);
+
+ label = TXT_NewLabel(buf);
+ TXT_SetFGColor(label, TXT_COLOR_BRIGHT_CYAN);
+
+ TXT_AddWidgets(table,
+ label,
+ TXT_NewInputBox(&chat_macros[(i + 1) % 10], 40),
+ NULL);
+ }
+
+ TXT_AddWidget(window, table);
+}
+
+void BindMultiplayerVariables(void)
+{
+ char buf[15];
+ int i;
+
+#ifdef FEATURE_MULTIPLAYER
+ M_BindVariable("player_name", &net_player_name);
+#endif
+
+ for (i=0; i<10; ++i)
+ {
+ sprintf(buf, "chatmacro%i", i);
+ M_BindVariable(buf, &chat_macros[i]);
+ }
+
+ switch (gamemission)
+ {
+ case doom:
+ M_BindChatControls(4);
+ key_multi_msgplayer[0] = 'g';
+ key_multi_msgplayer[1] = 'i';
+ key_multi_msgplayer[2] = 'b';
+ key_multi_msgplayer[3] = 'r';
+ break;
+
+ case heretic:
+ M_BindChatControls(4);
+ key_multi_msgplayer[0] = 'g';
+ key_multi_msgplayer[1] = 'y';
+ key_multi_msgplayer[2] = 'r';
+ key_multi_msgplayer[3] = 'b';
+ break;
+
+ case hexen:
+ M_BindChatControls(8);
+ key_multi_msgplayer[0] = 'b';
+ key_multi_msgplayer[1] = 'r';
+ key_multi_msgplayer[2] = 'y';
+ key_multi_msgplayer[3] = 'g';
+ key_multi_msgplayer[4] = 'j';
+ key_multi_msgplayer[5] = 'w';
+ key_multi_msgplayer[6] = 'h';
+ key_multi_msgplayer[7] = 'p';
+ break;
+
+ default:
+ break;
+ }
+}
+
diff --git a/src/setup/multiplayer.h b/src/setup/multiplayer.h
new file mode 100644
index 00000000..afc8a2a8
--- /dev/null
+++ b/src/setup/multiplayer.h
@@ -0,0 +1,36 @@
+// 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.
+//
+
+#ifndef SETUP_MULTIPLAYER_H
+#define SETUP_MULTIPLAYER_H
+
+void StartMultiGame(void);
+void WarpMenu(void);
+void JoinMultiGame(void);
+void MultiplayerConfig(void);
+
+void SetChatMacroDefaults(void);
+void SetPlayerNameDefault(void);
+
+void BindMultiplayerVariables(void);
+
+#endif /* #ifndef SETUP_MULTIPLAYER_H */
+
diff --git a/src/setup/setup-manifest.xml.in b/src/setup/setup-manifest.xml.in
new file mode 100644
index 00000000..ff879263
--- /dev/null
+++ b/src/setup/setup-manifest.xml.in
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+
+<!-- Magic manifest file that should make Windows Vista/7 not
+ attempt to gain elevated privileges for chocolate-setup.
+
+ Based on:
+
+ http://www.cygwin.com/ml/cygwin/2006-12/msg00580.html
+ -->
+
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <!-- The "name" field in this tag should be the same as the executable's
+ name -->
+ <assemblyIdentity version="0.0.0.0" processorArchitecture="X86"
+ name="@PROGRAM_PREFIX@setup.exe" type="win32"/>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="asInvoker" uiAccess="false" />
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+
+ <!-- Stop the Program Compatibility Assistant appearing: -->
+
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> <!-- 7 -->
+ <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> <!-- Vista -->
+ </application>
+ </compatibility>
+</assembly>
+
diff --git a/src/setup/setup.desktop.in b/src/setup/setup.desktop.in
new file mode 100644
index 00000000..79fb38be
--- /dev/null
+++ b/src/setup/setup.desktop.in
@@ -0,0 +1,7 @@
+[Desktop Entry]
+Name=@PACKAGE_NAME@ Setup
+Exec=@PROGRAM_PREFIX@setup
+Icon=@PROGRAM_PREFIX@setup
+Type=Application
+Comment=Setup tool for @PACKAGE_NAME@
+Categories=Settings;ConsoleOnly;
diff --git a/src/setup/setup_icon.c b/src/setup/setup_icon.c
new file mode 100644
index 00000000..1c18c56f
--- /dev/null
+++ b/src/setup/setup_icon.c
@@ -0,0 +1,262 @@
+static int setup_icon_w = 32;
+static int setup_icon_h = 32;
+
+static unsigned char setup_icon_data[] = {
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xa2,0x86,0x73,
+ 0xa9,0x8d,0x7a, 0xbc,0x9f,0x8c, 0xda,0xba,0xa0, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0xbe,0x8e,0x68, 0xd7,0xb9,0xa5, 0xeb,0xd8,0xcd, 0xd3,0xbf,0xae,
+ 0xbe,0xa1,0x8d, 0xeb,0xd8,0xcd, 0xc2,0x9d,0x86, 0x95,0x5d,0x38,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x78,0x7a,0x77, 0x78,0x7a,0x77,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x78,0x7a,0x77, 0x78,0x7a,0x77, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x9f,0x82,0x6a,
+ 0xc5,0x9e,0x81, 0xd1,0xb2,0x98, 0xd4,0xac,0x8e, 0xeb,0xd8,0xcd,
+ 0xc4,0x9b,0x79, 0xad,0x71,0x45, 0xd4,0xac,0x8e, 0xb9,0x93,0x76,
+ 0xa1,0x75,0x56, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x78,0x7a,0x77, 0x6d,0x6f,0x6c, 0xcb,0xce,0xca,
+ 0x51,0x52,0x50, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x5b,0x5d,0x5a,
+ 0xca,0xcc,0xc9, 0x77,0x79,0x76, 0x78,0x7a,0x77, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0xda,0xb4,0x9c, 0xd3,0xa3,0x83,
+ 0xaf,0x91,0x78, 0xa7,0x83,0x6d, 0xc4,0xa7,0x93, 0xee,0xe2,0xd5,
+ 0xeb,0xd8,0xcd, 0x8c,0x60,0x3d, 0x92,0x6f,0x59, 0xd0,0xa7,0x84,
+ 0x84,0x54,0x33, 0xba,0x83,0x5b, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x78,0x7a,0x77, 0xa0,0xa2,0x9f, 0xdf,0xe1,0xde,
+ 0x58,0x5a,0x58, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x64,0x65,0x63,
+ 0xdd,0xdf,0xdc, 0xa8,0xaa,0xa7, 0x78,0x7a,0x77, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0xd4,0xb5,0x9b, 0xc3,0x8c,0x63, 0xc4,0x94,0x6e,
+ 0x98,0x66,0x45, 0x78,0x50,0x2d, 0xd7,0xb9,0xa5, 0xee,0xdc,0xd1,
+ 0xc4,0x9b,0x79, 0xb6,0x80,0x58, 0x65,0x45,0x26, 0xb6,0x79,0x4d,
+ 0xcf,0xa5,0x83, 0x9a,0x6e,0x50, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x82,0x83,0x81, 0xbb,0xbd,0xba, 0xde,0xe0,0xdd,
+ 0x58,0x5a,0x58, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x64,0x65,0x63,
+ 0xdc,0xde,0xdb, 0xc4,0xc6,0xc3, 0x78,0x7a,0x77, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x9e,0x7b,0x65, 0xbc,0x8c,0x67, 0xaa,0x7d,0x5e, 0xa1,0x75,0x56,
+ 0x89,0x5f,0x41, 0xc4,0xa7,0x93, 0xb7,0x88,0x63, 0x90,0x6c,0x51,
+ 0x79,0x4b,0x2b, 0x8c,0x5b,0x34, 0x76,0x4e,0x31, 0x7f,0x50,0x30,
+ 0xcf,0xa5,0x83, 0xd4,0xac,0x8e, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x77,0x79,0x76, 0xd2,0xd4,0xd1, 0xde,0xe0,0xdd,
+ 0x64,0x65,0x63, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x68,0x69,0x67,
+ 0xdb,0xdd,0xda, 0xda,0xdc,0xd9, 0x78,0x7a,0x77, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xd8,0xb8,0x9e,
+ 0xd4,0xac,0x8e, 0xc5,0x9e,0x81, 0xab,0x7e,0x5f, 0x9c,0x6f,0x4b,
+ 0xbe,0xa1,0x8d, 0x8c,0x60,0x3d, 0x6e,0x47,0x2b, 0x87,0x5e,0x40,
+ 0x5a,0x3b,0x23, 0x68,0x42,0x26, 0x65,0x40,0x23, 0x53,0x36,0x22,
+ 0x7e,0x55,0x38, 0xce,0x9f,0x7e, 0xc3,0x8c,0x63, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x77,0x79,0x76, 0xe1,0xe4,0xe0, 0xe1,0xe4,0xe0,
+ 0xc4,0xc6,0xc3, 0x83,0x85,0x82, 0x8c,0x8d,0x8a, 0xd2,0xd4,0xd1,
+ 0xee,0xdc,0xd1, 0xe1,0xe4,0xe0, 0x78,0x7a,0x77, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xcb,0x9a,0x74,
+ 0xb6,0x80,0x58, 0x8c,0x60,0x3d, 0x76,0x4e,0x31, 0x88,0x57,0x31,
+ 0x83,0x53,0x33, 0x84,0x54,0x33, 0x95,0x5d,0x38, 0x79,0x4b,0x2b,
+ 0x5c,0x38,0x22, 0x84,0x54,0x33, 0x55,0x37,0x1e, 0x96,0x6b,0x4d,
+ 0xb4,0x7f,0x5c, 0xba,0x83,0x5b, 0xb8,0x7b,0x4f, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x9a,0x9b,0x98, 0xd9,0xdb,0xd7,
+ 0xe1,0xe4,0xe0, 0xde,0xe0,0xdd, 0xdd,0xdf,0xdc, 0xe0,0xe2,0xdf,
+ 0xda,0xdc,0xd9, 0xa3,0xa5,0xa1, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xc0,0x89,0x60,
+ 0x89,0x5f,0x41, 0x84,0x54,0x33, 0x84,0x54,0x33, 0x8a,0x5a,0x39,
+ 0x8f,0x5d,0x37, 0x78,0x50,0x2d, 0x8c,0x5b,0x34, 0x5a,0x3b,0x23,
+ 0x5e,0x3f,0x27, 0x76,0x4e,0x31, 0x97,0x64,0x3d, 0x74,0x4b,0x29,
+ 0x78,0x50,0x2d, 0x7b,0x4d,0x2c, 0xb6,0x80,0x58, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x64,0x65,0x63,
+ 0xbe,0xc1,0xbd, 0xee,0xdc,0xd1, 0xe1,0xe4,0xe0, 0xc2,0xc4,0xc1,
+ 0x68,0x69,0x67, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x95,0x62,0x3b,
+ 0x84,0x54,0x33, 0x88,0x57,0x31, 0x7e,0x6e,0x64, 0xc4,0x94,0x6e,
+ 0x76,0x4e,0x31, 0x90,0x6c,0x51, 0xa1,0x7c,0x60, 0x9a,0x6e,0x50,
+ 0x95,0x5d,0x38, 0xbc,0x7f,0x53, 0xad,0x71,0x45, 0x76,0x4e,0x31,
+ 0x53,0x36,0x22, 0x4b,0x2f,0x1c, 0x70,0x49,0x2c, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x51,0x52,0x50, 0xd2,0xd4,0xd1, 0xe0,0xe2,0xdf, 0x77,0x79,0x76,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x84,0x54,0x33,
+ 0xa3,0x86,0x6e, 0xc4,0x94,0x6e, 0x88,0x64,0x44, 0xbc,0x8c,0x67,
+ 0x9c,0x6f,0x4b, 0xa1,0x6d,0x45, 0x93,0x60,0x3a, 0xad,0x71,0x3f,
+ 0xb4,0x7f,0x5c, 0xbc,0x8c,0x67, 0xc0,0x89,0x60, 0xb3,0x76,0x4b,
+ 0xb8,0x7b,0x4f, 0x88,0x64,0x44, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x4a,0x4b,0x49, 0xdc,0xde,0xdb, 0xe1,0xe4,0xe0, 0x79,0x7b,0x78,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xa7,0x83,0x6d,
+ 0xac,0x86,0x6a, 0x76,0x4e,0x31, 0x84,0x54,0x33, 0x5c,0x38,0x22,
+ 0x57,0x38,0x20, 0x6c,0x46,0x29, 0x95,0x62,0x3b, 0xa3,0x6e,0x41,
+ 0xb3,0x76,0x4b, 0xb8,0x7b,0x4f, 0x9d,0x64,0x3f, 0x7e,0x4f,0x2f,
+ 0x63,0x3e,0x27, 0x95,0x62,0x3b, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x51,0x52,0x50, 0xdd,0xdf,0xdc, 0xe0,0xe2,0xdf, 0x82,0x83,0x81,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xb6,0x80,0x58,
+ 0x98,0x74,0x59, 0x67,0x41,0x25, 0x4b,0x35,0x25, 0x81,0x52,0x31,
+ 0x76,0x4e,0x31, 0x7b,0x4d,0x2c, 0x7e,0x4f,0x2f, 0x84,0x54,0x33,
+ 0x8f,0x5d,0x37, 0x95,0x5d,0x38, 0x78,0x50,0x2d, 0x65,0x45,0x26,
+ 0x65,0x40,0x23, 0x7e,0x4f,0x2f, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x55,0x56,0x54, 0xee,0xdc,0xd1, 0xde,0xe0,0xdd, 0x8c,0x8d,0x8a,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xb4,0x7f,0x5c,
+ 0x86,0x56,0x35, 0x8c,0x60,0x3d, 0x89,0x5f,0x41, 0x63,0x44,0x2b,
+ 0x57,0x38,0x20, 0x86,0x56,0x35, 0x88,0x57,0x31, 0x95,0x5d,0x38,
+ 0x97,0x64,0x3d, 0x63,0x3e,0x27, 0x50,0x33,0x20, 0x78,0x50,0x2d,
+ 0x78,0x50,0x2d, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x58,0x5a,0x58, 0xe0,0xe2,0xdf, 0xde,0xe0,0xdd, 0x8c,0x8d,0x8a,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x86,0x56,0x35, 0x63,0x3e,0x27, 0xa8,0x6d,0x42, 0x50,0x33,0x20,
+ 0x63,0x3e,0x27, 0x74,0x4b,0x29, 0x53,0x36,0x22, 0x78,0x50,0x2d,
+ 0x42,0x30,0x14, 0x4d,0x3e,0x15, 0x4d,0x3e,0x15, 0x7c,0x5b,0x29,
+ 0x8a,0x71,0x27, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x64,0x65,0x63, 0xe1,0xe4,0xe0, 0xe1,0xe4,0xe0, 0x91,0x93,0x90,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x6e,0x47,0x2b, 0x65,0x45,0x26, 0x5d,0x42,0x22, 0x65,0x45,0x26,
+ 0x78,0x50,0x2d, 0x5d,0x42,0x22, 0x5e,0x3f,0x27, 0x4d,0x3e,0x15,
+ 0x4d,0x3e,0x15, 0x67,0x58,0x21, 0x4f,0x44,0x19, 0x5f,0x51,0x19,
+ 0x8a,0x76,0x2a, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x68,0x69,0x67, 0xdf,0xe1,0xde, 0xe0,0xe2,0xdf, 0x9a,0x9b,0x98,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x8a,0x71,0x27, 0x53,0x46,0x15,
+ 0x53,0x46,0x15, 0x67,0x58,0x21, 0x58,0x4c,0x1b, 0x5b,0x4f,0x1d,
+ 0x5b,0x4f,0x1d, 0x67,0x58,0x21, 0x5b,0x4f,0x1d, 0x8a,0x71,0x27,
+ 0x8c,0x77,0x24, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x70,0x72,0x6f, 0xe1,0xe4,0xe0, 0xe1,0xe4,0xe0, 0x9a,0x9b,0x98,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0xac,0x93,0x39, 0x7e,0x66,0x23,
+ 0x69,0x5a,0x1b, 0x6b,0x5b,0x1d, 0x67,0x58,0x21, 0x53,0x46,0x15,
+ 0x4d,0x3e,0x15, 0x9f,0x88,0x35, 0xb7,0x9c,0x3b, 0xac,0x93,0x39,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x77,0x79,0x76, 0xdf,0xe1,0xde, 0xe0,0xe2,0xdf, 0xa3,0xa5,0xa1,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0xb1,0x97,0x36, 0x6f,0x5f,0x21,
+ 0x7e,0x66,0x23, 0x8c,0x77,0x24, 0x84,0x70,0x24, 0x78,0x67,0x22,
+ 0x8e,0x79,0x26, 0x8a,0x71,0x27, 0xb7,0x9c,0x3b, 0x9b,0x84,0x29,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x82,0x83,0x81, 0xe1,0xe4,0xe0, 0xe1,0xe4,0xe0, 0xa8,0xaa,0xa7,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0xac,0x93,0x39, 0x89,0x75,0x29,
+ 0x7e,0x66,0x23, 0x75,0x64,0x1f, 0x94,0x7e,0x2b, 0x7e,0x66,0x23,
+ 0x9f,0x88,0x35, 0xb6,0x9d,0x4a, 0xb6,0x9d,0x4a, 0xc4,0xa8,0x3f,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x82,0x83,0x81, 0xe0,0xe2,0xdf, 0xde,0xe0,0xdd, 0xab,0xad,0xaa,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x80,0x6d,0x28,
+ 0xac,0x93,0x39, 0x97,0x82,0x36, 0xac,0x94,0x41, 0xac,0x93,0x39,
+ 0x97,0x82,0x36, 0xa2,0x8a,0x30, 0xbd,0xa3,0x48, 0x8a,0x71,0x27,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x77,0x79,0x76, 0xe0,0xe2,0xdf, 0xd8,0xda,0xd6, 0xab,0xad,0xaa,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xae,0x95,0x33,
+ 0x7e,0x66,0x23, 0x9f,0x88,0x35, 0x9f,0x88,0x35, 0x7e,0x66,0x23,
+ 0x8a,0x71,0x27, 0xaf,0x96,0x3c, 0xbd,0xa2,0x41, 0x8a,0x76,0x2a,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x68,0x69,0x67,
+ 0xc2,0xc4,0xc1, 0xe0,0xe2,0xdf, 0xdf,0xe1,0xde, 0xbe,0xc1,0xbd,
+ 0x5f,0x61,0x5e, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xaf,0x96,0x3c,
+ 0x87,0x73,0x27, 0xb2,0x99,0x3f, 0x6f,0x5f,0x21, 0xa8,0x90,0x36,
+ 0x97,0x82,0x36, 0x9f,0x88,0x35, 0xb7,0x9c,0x3b, 0x8c,0x77,0x24,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0xa3,0xa5,0xa1, 0xeb,0xd8,0xcd,
+ 0xe0,0xe2,0xdf, 0xee,0xdc,0xd1, 0xdf,0xe1,0xde, 0xe1,0xe4,0xe0,
+ 0xda,0xdc,0xd9, 0x9a,0x9b,0x98, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xb2,0x99,0x3f,
+ 0xaf,0x96,0x3c, 0x96,0x80,0x2d, 0xbd,0xa3,0x48, 0x97,0x82,0x36,
+ 0xb6,0x9d,0x4a, 0xb8,0xa0,0x4c, 0xc1,0xa7,0x4c, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x78,0x7a,0x77, 0xdf,0xe1,0xde, 0xe0,0xe2,0xdf,
+ 0xd2,0xd4,0xd1, 0x8c,0x8d,0x8a, 0x82,0x83,0x81, 0xc7,0xc9,0xc6,
+ 0xe1,0xe4,0xe0, 0xe1,0xe4,0xe0, 0x78,0x7a,0x77, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x96,0x80,0x2d, 0xb5,0x9c,0x49, 0xb2,0x99,0x3f, 0xb2,0x9a,0x47,
+ 0xb0,0x97,0x3d, 0xc1,0xa7,0x4c, 0x96,0x80,0x2d, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x78,0x7a,0x77, 0xd9,0xdb,0xd7, 0xda,0xdc,0xd9,
+ 0x68,0x69,0x67, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x64,0x65,0x63,
+ 0xdd,0xdf,0xdc, 0xd2,0xd4,0xd1, 0x78,0x7a,0x77, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x9c,0x85,0x2b, 0xaa,0x91,0x2f, 0xb1,0x97,0x36, 0xa2,0x8a,0x30,
+ 0x7e,0x66,0x23, 0xb1,0x97,0x36, 0xb4,0x99,0x30, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x79,0x7b,0x78, 0xc4,0xc6,0xc3, 0xd9,0xdb,0xd7,
+ 0x64,0x65,0x63, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x55,0x56,0x54,
+ 0xdf,0xe1,0xde, 0xbb,0xbd,0xba, 0x78,0x7a,0x77, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x9f,0x88,0x35, 0xb8,0x9e,0x44, 0x8a,0x71,0x27, 0xa6,0x8f,0x3c,
+ 0xbd,0xa3,0x48, 0x96,0x80,0x2d, 0x9f,0x88,0x35, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x82,0x83,0x81, 0xa8,0xaa,0xa7, 0xdf,0xe1,0xde,
+ 0x64,0x65,0x63, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x58,0x5a,0x58,
+ 0xee,0xdc,0xd1, 0xa0,0xa2,0x9f, 0x78,0x7a,0x77, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0xb0,0x97,0x3d, 0xb7,0x9c,0x3b, 0xac,0x94,0x41, 0xb2,0x99,0x3f,
+ 0xb6,0x9b,0x32, 0xb7,0x9c,0x3b, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x77,0x79,0x76, 0x77,0x79,0x76, 0xca,0xcc,0xc9,
+ 0x5b,0x5d,0x5a, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x51,0x52,0x50,
+ 0xcb,0xce,0xca, 0x68,0x69,0x67, 0x79,0x7b,0x78, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0xbd,0xa2,0x41, 0xa6,0x8f,0x3c, 0xb0,0x97,0x3d, 0xb2,0x9a,0x47,
+ 0xac,0x93,0x39, 0x9f,0x88,0x35, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x77,0x79,0x76, 0x78,0x7a,0x77,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x82,0x83,0x81, 0x79,0x7b,0x78, 0x00,0x00,0x00, 0x00,0x00,0x00,
+
+};
diff --git a/src/setup/sound.c b/src/setup/sound.c
new file mode 100644
index 00000000..9e13edd9
--- /dev/null
+++ b/src/setup/sound.c
@@ -0,0 +1,261 @@
+// 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.
+//
+
+// Sound control menu
+
+#include <stdlib.h>
+
+#include "SDL_mixer.h"
+
+#include "textscreen.h"
+#include "m_config.h"
+
+#include "i_sound.h"
+#include "mode.h"
+#include "sound.h"
+
+typedef enum
+{
+ SFXMODE_DISABLED,
+ SFXMODE_DIGITAL,
+ SFXMODE_PCSPEAKER,
+ NUM_SFXMODES
+} sfxmode_t;
+
+static char *sfxmode_strings[] =
+{
+ "Disabled",
+ "Digital",
+ "PC speaker"
+};
+
+typedef enum
+{
+ MUSICMODE_DISABLED,
+ MUSICMODE_OPL,
+ MUSICMODE_NATIVE,
+ MUSICMODE_CD,
+ NUM_MUSICMODES
+} musicmode_t;
+
+static char *musicmode_strings[] =
+{
+ "Disabled",
+ "OPL (Adlib/SB)",
+ "Native MIDI",
+ "CD audio"
+};
+
+// Config file variables:
+
+int snd_sfxdevice = SNDDEVICE_SB;
+int snd_musicdevice = SNDDEVICE_GENMIDI;
+int snd_samplerate = 44100;
+int opl_io_port = 0x388;
+
+static int numChannels = 8;
+static int sfxVolume = 15;
+static int musicVolume = 15;
+static int use_libsamplerate = 0;
+
+// DOS specific variables: these are unused but should be maintained
+// so that the config file can be shared between chocolate
+// doom and doom.exe
+
+static int snd_sbport = 0;
+static int snd_sbirq = 0;
+static int snd_sbdma = 0;
+static int snd_mport = 0;
+
+// GUI variables:
+
+static int snd_sfxmode;
+static int snd_musicmode;
+
+static void UpdateSndDevices(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(data))
+{
+ switch (snd_sfxmode)
+ {
+ case SFXMODE_DISABLED:
+ snd_sfxdevice = SNDDEVICE_NONE;
+ break;
+ case SFXMODE_PCSPEAKER:
+ snd_sfxdevice = SNDDEVICE_PCSPEAKER;
+ break;
+ case SFXMODE_DIGITAL:
+ snd_sfxdevice = SNDDEVICE_SB;
+ break;
+ }
+
+ switch (snd_musicmode)
+ {
+ case MUSICMODE_DISABLED:
+ snd_musicdevice = SNDDEVICE_NONE;
+ break;
+ case MUSICMODE_NATIVE:
+ snd_musicdevice = SNDDEVICE_GENMIDI;
+ break;
+ case MUSICMODE_OPL:
+ snd_musicdevice = SNDDEVICE_SB;
+ break;
+ case MUSICMODE_CD:
+ break;
+ }
+}
+
+void ConfigSound(void)
+{
+ txt_window_t *window;
+ txt_table_t *sfx_table;
+ txt_table_t *music_table;
+ txt_dropdown_list_t *sfx_mode_control;
+ txt_dropdown_list_t *music_mode_control;
+ int num_sfx_modes, num_music_modes;
+
+ // Work out what sfx mode we are currently using:
+
+ if (snd_sfxdevice == SNDDEVICE_PCSPEAKER)
+ {
+ snd_sfxmode = SFXMODE_PCSPEAKER;
+ }
+ else if (snd_sfxdevice >= SNDDEVICE_SB)
+ {
+ snd_sfxmode = SFXMODE_DIGITAL;
+ }
+ else
+ {
+ snd_sfxmode = SFXMODE_DISABLED;
+ }
+
+ // Is music enabled?
+
+ if (snd_musicdevice == SNDDEVICE_GENMIDI)
+ {
+ snd_musicmode = MUSICMODE_NATIVE;
+ }
+ else if (snd_musicmode == SNDDEVICE_CD)
+ {
+ snd_musicmode = MUSICMODE_CD;
+ }
+ else if (snd_musicdevice == SNDDEVICE_SB
+ || snd_musicdevice == SNDDEVICE_ADLIB
+ || snd_musicdevice == SNDDEVICE_AWE32)
+ {
+ snd_musicmode = MUSICMODE_OPL;
+ }
+ else
+ {
+ snd_musicmode = MUSICMODE_DISABLED;
+ }
+
+ // Doom has PC speaker sound effects, but others do not:
+
+ if (gamemission == doom)
+ {
+ num_sfx_modes = NUM_SFXMODES;
+ }
+ else
+ {
+ num_sfx_modes = NUM_SFXMODES - 1;
+ }
+
+ // Hexen has CD audio; others do not.
+
+ if (gamemission == hexen)
+ {
+ num_music_modes = NUM_MUSICMODES;
+ }
+ else
+ {
+ num_music_modes = NUM_MUSICMODES - 1;
+ }
+
+ // Build the window
+
+ window = TXT_NewWindow("Sound configuration");
+
+ TXT_AddWidgets(window,
+ TXT_NewSeparator("Sound effects"),
+ sfx_table = TXT_NewTable(2),
+ TXT_NewSeparator("Music"),
+ music_table = TXT_NewTable(2),
+ NULL);
+
+ TXT_SetColumnWidths(sfx_table, 20, 14);
+
+ TXT_AddWidgets(sfx_table,
+ TXT_NewLabel("Sound effects"),
+ sfx_mode_control = TXT_NewDropdownList(&snd_sfxmode,
+ sfxmode_strings,
+ num_sfx_modes),
+ TXT_NewLabel("Sound channels"),
+ TXT_NewSpinControl(&numChannels, 1, 8),
+ TXT_NewLabel("SFX volume"),
+ TXT_NewSpinControl(&sfxVolume, 0, 15),
+ NULL);
+
+ TXT_SetColumnWidths(music_table, 20, 14);
+
+ TXT_AddWidgets(music_table,
+ TXT_NewLabel("Music"),
+ music_mode_control = TXT_NewDropdownList(&snd_musicmode,
+ musicmode_strings,
+ num_music_modes),
+ TXT_NewLabel("Music volume"),
+ TXT_NewSpinControl(&musicVolume, 0, 15),
+ NULL);
+
+ TXT_SignalConnect(sfx_mode_control, "changed", UpdateSndDevices, NULL);
+ TXT_SignalConnect(music_mode_control, "changed", UpdateSndDevices, NULL);
+}
+
+void BindSoundVariables(void)
+{
+ M_BindVariable("snd_sfxdevice", &snd_sfxdevice);
+ M_BindVariable("snd_musicdevice", &snd_musicdevice);
+ M_BindVariable("snd_channels", &numChannels);
+ M_BindVariable("sfx_volume", &sfxVolume);
+ M_BindVariable("music_volume", &musicVolume);
+ M_BindVariable("snd_samplerate", &snd_samplerate);
+ M_BindVariable("use_libsamplerate", &use_libsamplerate);
+
+ M_BindVariable("snd_sbport", &snd_sbport);
+ M_BindVariable("snd_sbirq", &snd_sbirq);
+ M_BindVariable("snd_sbdma", &snd_sbdma);
+ M_BindVariable("snd_mport", &snd_mport);
+
+ // Before SDL_mixer version 1.2.11, MIDI music caused the game
+ // to crash when it looped. If this is an old SDL_mixer version,
+ // disable MIDI.
+
+#ifdef __MACOSX__
+ {
+ const SDL_version *v = Mix_Linked_Version();
+
+ if (SDL_VERSIONNUM(v->major, v->minor, v->patch)
+ < SDL_VERSIONNUM(1, 2, 11))
+ {
+ snd_musicdevice = SNDDEVICE_NONE;
+ }
+ }
+#endif
+}
+
diff --git a/src/setup/sound.h b/src/setup/sound.h
new file mode 100644
index 00000000..0b183502
--- /dev/null
+++ b/src/setup/sound.h
@@ -0,0 +1,29 @@
+// 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.
+//
+
+#ifndef SETUP_SOUND_H
+#define SETUP_SOUND_H
+
+void ConfigSound(void);
+void BindSoundVariables(void);
+
+#endif /* #ifndef SETUP_SOUND_H */
+
diff --git a/src/setup/txt_joybinput.c b/src/setup/txt_joybinput.c
new file mode 100644
index 00000000..9ad26a45
--- /dev/null
+++ b/src/setup/txt_joybinput.c
@@ -0,0 +1,230 @@
+// 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "SDL_joystick.h"
+
+#include "doomkeys.h"
+#include "joystick.h"
+
+#include "txt_joybinput.h"
+#include "txt_gui.h"
+#include "txt_io.h"
+#include "txt_label.h"
+#include "txt_sdl.h"
+#include "txt_window.h"
+
+#define JOYSTICK_INPUT_WIDTH 10
+
+// Called in response to SDL events when the prompt window is open:
+
+static int EventCallback(SDL_Event *event, TXT_UNCAST_ARG(joystick_input))
+{
+ TXT_CAST_ARG(txt_joystick_input_t, joystick_input);
+
+ // Got the joystick button press?
+
+ if (event->type == SDL_JOYBUTTONDOWN)
+ {
+ *joystick_input->variable = event->jbutton.button;
+
+ if (joystick_input->check_conflicts)
+ {
+ TXT_EmitSignal(joystick_input, "set");
+ }
+
+ TXT_CloseWindow(joystick_input->prompt_window);
+ return 1;
+ }
+
+ return 0;
+}
+
+// When the prompt window is closed, disable the event callback function;
+// we are no longer interested in receiving notification of events.
+
+static void PromptWindowClosed(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(joystick))
+{
+ TXT_CAST_ARG(SDL_Joystick, joystick);
+
+ SDL_JoystickClose(joystick);
+ TXT_SDL_SetEventCallback(NULL, NULL);
+ SDL_JoystickEventState(SDL_DISABLE);
+ SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
+}
+
+static void OpenErrorWindow(void)
+{
+ txt_window_t *window;
+
+ window = TXT_NewWindow(NULL);
+
+ TXT_AddWidget(window, TXT_NewLabel("Please configure a joystick first!"));
+
+ TXT_SetWindowAction(window, TXT_HORIZ_LEFT, NULL);
+ TXT_SetWindowAction(window, TXT_HORIZ_CENTER,
+ TXT_NewWindowEscapeAction(window));
+ TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, NULL);
+}
+
+static void OpenPromptWindow(txt_joystick_input_t *joystick_input)
+{
+ txt_window_t *window;
+ txt_label_t *label;
+ SDL_Joystick *joystick;
+
+ // Silently update when the shift button is held down.
+
+ joystick_input->check_conflicts = !TXT_GetModifierState(TXT_MOD_SHIFT);
+
+ if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
+ {
+ return;
+ }
+
+ // Check the current joystick is valid
+
+ joystick = SDL_JoystickOpen(joystick_index);
+
+ if (joystick == NULL)
+ {
+ OpenErrorWindow();
+ return;
+ }
+
+ // Open the prompt window
+
+ window = TXT_NewWindow(NULL);
+ TXT_SetWindowAction(window, TXT_HORIZ_LEFT, NULL);
+ TXT_SetWindowAction(window, TXT_HORIZ_CENTER,
+ TXT_NewWindowAbortAction(window));
+ TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, NULL);
+
+ label = TXT_NewLabel("Press the new joystick button...");
+
+ TXT_AddWidget(window, label);
+ TXT_SetWidgetAlign(label, TXT_HORIZ_CENTER);
+ TXT_SDL_SetEventCallback(EventCallback, joystick_input);
+ TXT_SignalConnect(window, "closed", PromptWindowClosed, joystick);
+ joystick_input->prompt_window = window;
+
+ SDL_JoystickEventState(SDL_ENABLE);
+}
+
+static void TXT_JoystickInputSizeCalc(TXT_UNCAST_ARG(joystick_input))
+{
+ TXT_CAST_ARG(txt_joystick_input_t, joystick_input);
+
+ // All joystickinputs are the same size.
+
+ joystick_input->widget.w = JOYSTICK_INPUT_WIDTH;
+ joystick_input->widget.h = 1;
+}
+
+static void GetJoystickButtonDescription(int button, char *buf)
+{
+ sprintf(buf, "BUTTON #%i", button + 1);
+}
+
+static void TXT_JoystickInputDrawer(TXT_UNCAST_ARG(joystick_input), int selected)
+{
+ TXT_CAST_ARG(txt_joystick_input_t, joystick_input);
+ char buf[20];
+ int i;
+
+ if (*joystick_input->variable < 0)
+ {
+ strcpy(buf, "(none)");
+ }
+ else
+ {
+ GetJoystickButtonDescription(*joystick_input->variable, buf);
+ }
+
+ TXT_SetWidgetBG(joystick_input, selected);
+ TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
+
+ TXT_DrawString(buf);
+
+ for (i=strlen(buf); i<JOYSTICK_INPUT_WIDTH; ++i)
+ {
+ TXT_DrawString(" ");
+ }
+}
+
+static void TXT_JoystickInputDestructor(TXT_UNCAST_ARG(joystick_input))
+{
+}
+
+static int TXT_JoystickInputKeyPress(TXT_UNCAST_ARG(joystick_input), int joystick)
+{
+ TXT_CAST_ARG(txt_joystick_input_t, joystick_input);
+
+ if (joystick == KEY_ENTER)
+ {
+ // Open a window to prompt for the new joystick press
+
+ OpenPromptWindow(joystick_input);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static void TXT_JoystickInputMousePress(TXT_UNCAST_ARG(widget), int x, int y, int b)
+{
+ TXT_CAST_ARG(txt_joystick_input_t, widget);
+
+ // Clicking is like pressing enter
+
+ if (b == TXT_MOUSE_LEFT)
+ {
+ TXT_JoystickInputKeyPress(widget, KEY_ENTER);
+ }
+}
+
+txt_widget_class_t txt_joystick_input_class =
+{
+ TXT_AlwaysSelectable,
+ TXT_JoystickInputSizeCalc,
+ TXT_JoystickInputDrawer,
+ TXT_JoystickInputKeyPress,
+ TXT_JoystickInputDestructor,
+ TXT_JoystickInputMousePress,
+ NULL,
+};
+
+txt_joystick_input_t *TXT_NewJoystickInput(int *variable)
+{
+ txt_joystick_input_t *joystick_input;
+
+ joystick_input = malloc(sizeof(txt_joystick_input_t));
+
+ TXT_InitWidget(joystick_input, &txt_joystick_input_class);
+ joystick_input->variable = variable;
+
+ return joystick_input;
+}
+
diff --git a/src/setup/txt_joybinput.h b/src/setup/txt_joybinput.h
new file mode 100644
index 00000000..69ec4a1f
--- /dev/null
+++ b/src/setup/txt_joybinput.h
@@ -0,0 +1,47 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2007 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.
+//
+
+#ifndef TXT_JOYB_INPUT_H
+#define TXT_JOYB_INPUT_H
+
+typedef struct txt_joystick_input_s txt_joystick_input_t;
+
+#include "txt_widget.h"
+#include "txt_window.h"
+
+//
+// A joystick input is like an input box. When selected, a box pops up
+// allowing a joystick button to be pressed to select it.
+//
+
+struct txt_joystick_input_s
+{
+ txt_widget_t widget;
+ int *variable;
+ txt_window_t *prompt_window;
+ int check_conflicts;
+};
+
+txt_joystick_input_t *TXT_NewJoystickInput(int *variable);
+
+#endif /* #ifndef TXT_JOYB_INPUT_H */
+
+
diff --git a/src/setup/txt_keyinput.c b/src/setup/txt_keyinput.c
new file mode 100644
index 00000000..6f1ee4dd
--- /dev/null
+++ b/src/setup/txt_keyinput.c
@@ -0,0 +1,195 @@
+// 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>
+
+#include "doomkeys.h"
+
+#include "txt_keyinput.h"
+#include "txt_gui.h"
+#include "txt_io.h"
+#include "txt_label.h"
+#include "txt_window.h"
+
+#define KEY_INPUT_WIDTH 8
+
+static int KeyPressCallback(txt_window_t *window, int key,
+ TXT_UNCAST_ARG(key_input))
+{
+ TXT_CAST_ARG(txt_key_input_t, key_input);
+
+ if (key != KEY_ESCAPE)
+ {
+ // Got the key press. Save to the variable and close the window.
+
+ *key_input->variable = key;
+
+ if (key_input->check_conflicts)
+ {
+ TXT_EmitSignal(key_input, "set");
+ }
+
+ TXT_CloseWindow(window);
+
+ // Re-enable key mappings now that we have the key
+
+ TXT_EnableKeyMapping(1);
+
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+static void ReleaseGrab(TXT_UNCAST_ARG(window), TXT_UNCAST_ARG(unused))
+{
+ SDL_WM_GrabInput(SDL_GRAB_OFF);
+}
+
+static void OpenPromptWindow(txt_key_input_t *key_input)
+{
+ txt_window_t *window;
+ txt_label_t *label;
+
+ // Silently update when the shift button is held down.
+
+ key_input->check_conflicts = !TXT_GetModifierState(TXT_MOD_SHIFT);
+
+ window = TXT_NewWindow(NULL);
+ TXT_SetWindowAction(window, TXT_HORIZ_LEFT, NULL);
+ TXT_SetWindowAction(window, TXT_HORIZ_CENTER,
+ TXT_NewWindowAbortAction(window));
+ TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, NULL);
+
+ label = TXT_NewLabel("Press the new key...");
+
+ TXT_AddWidget(window, label);
+ TXT_SetWidgetAlign(label, TXT_HORIZ_CENTER);
+
+ TXT_SetKeyListener(window, KeyPressCallback, key_input);
+
+ // Disable key mappings while we prompt for the key press
+
+ TXT_EnableKeyMapping(0);
+
+ // Grab input while reading the key. On Windows Mobile
+ // handheld devices, the hardware keypresses are only
+ // detected when input is grabbed.
+
+ SDL_WM_GrabInput(SDL_GRAB_ON);
+ TXT_SignalConnect(window, "closed", ReleaseGrab, NULL);
+}
+
+static void TXT_KeyInputSizeCalc(TXT_UNCAST_ARG(key_input))
+{
+ TXT_CAST_ARG(txt_key_input_t, key_input);
+
+ // All keyinputs are the same size.
+
+ key_input->widget.w = KEY_INPUT_WIDTH;
+ key_input->widget.h = 1;
+}
+
+
+static void TXT_KeyInputDrawer(TXT_UNCAST_ARG(key_input), int selected)
+{
+ TXT_CAST_ARG(txt_key_input_t, key_input);
+ char buf[20];
+ int i;
+
+ if (*key_input->variable == 0)
+ {
+ strcpy(buf, "(none)");
+ }
+ else
+ {
+ TXT_GetKeyDescription(*key_input->variable, buf);
+ }
+
+ TXT_SetWidgetBG(key_input, selected);
+ TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
+
+ TXT_DrawString(buf);
+
+ for (i=strlen(buf); i<KEY_INPUT_WIDTH; ++i)
+ {
+ TXT_DrawString(" ");
+ }
+}
+
+static void TXT_KeyInputDestructor(TXT_UNCAST_ARG(key_input))
+{
+}
+
+static int TXT_KeyInputKeyPress(TXT_UNCAST_ARG(key_input), int key)
+{
+ TXT_CAST_ARG(txt_key_input_t, key_input);
+
+ if (key == KEY_ENTER)
+ {
+ // Open a window to prompt for the new key press
+
+ OpenPromptWindow(key_input);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static void TXT_KeyInputMousePress(TXT_UNCAST_ARG(widget), int x, int y, int b)
+{
+ TXT_CAST_ARG(txt_key_input_t, widget);
+
+ // Clicking is like pressing enter
+
+ if (b == TXT_MOUSE_LEFT)
+ {
+ TXT_KeyInputKeyPress(widget, KEY_ENTER);
+ }
+}
+
+txt_widget_class_t txt_key_input_class =
+{
+ TXT_AlwaysSelectable,
+ TXT_KeyInputSizeCalc,
+ TXT_KeyInputDrawer,
+ TXT_KeyInputKeyPress,
+ TXT_KeyInputDestructor,
+ TXT_KeyInputMousePress,
+ NULL,
+};
+
+txt_key_input_t *TXT_NewKeyInput(int *variable)
+{
+ txt_key_input_t *key_input;
+
+ key_input = malloc(sizeof(txt_key_input_t));
+
+ TXT_InitWidget(key_input, &txt_key_input_class);
+ key_input->variable = variable;
+
+ return key_input;
+}
+
diff --git a/src/setup/txt_keyinput.h b/src/setup/txt_keyinput.h
new file mode 100644
index 00000000..5df0b2e3
--- /dev/null
+++ b/src/setup/txt_keyinput.h
@@ -0,0 +1,45 @@
+// 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.
+//
+
+#ifndef TXT_KEY_INPUT_H
+#define TXT_KEY_INPUT_H
+
+typedef struct txt_key_input_s txt_key_input_t;
+
+#include "txt_widget.h"
+
+//
+// A key input is like an input box. When selected, a box pops up
+// allowing a key to be selected.
+//
+
+struct txt_key_input_s
+{
+ txt_widget_t widget;
+ int *variable;
+ int check_conflicts;
+};
+
+txt_key_input_t *TXT_NewKeyInput(int *variable);
+
+#endif /* #ifndef TXT_KEY_INPUT_H */
+
+
diff --git a/src/setup/txt_mouseinput.c b/src/setup/txt_mouseinput.c
new file mode 100644
index 00000000..c3e17299
--- /dev/null
+++ b/src/setup/txt_mouseinput.c
@@ -0,0 +1,187 @@
+// 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "doomkeys.h"
+
+#include "txt_mouseinput.h"
+#include "txt_gui.h"
+#include "txt_io.h"
+#include "txt_label.h"
+#include "txt_window.h"
+
+#define MOUSE_INPUT_WIDTH 8
+
+static int MousePressCallback(txt_window_t *window,
+ int x, int y, int b,
+ TXT_UNCAST_ARG(mouse_input))
+{
+ TXT_CAST_ARG(txt_mouse_input_t, mouse_input);
+
+ // Got the mouse press. Save to the variable and close the window.
+
+ *mouse_input->variable = b - TXT_MOUSE_BASE;
+
+ if (mouse_input->check_conflicts)
+ {
+ TXT_EmitSignal(mouse_input, "set");
+ }
+
+ TXT_CloseWindow(window);
+
+ return 1;
+}
+
+static void OpenPromptWindow(txt_mouse_input_t *mouse_input)
+{
+ txt_window_t *window;
+ txt_label_t *label;
+
+ // Silently update when the shift key is held down.
+ mouse_input->check_conflicts = !TXT_GetModifierState(TXT_MOD_SHIFT);
+
+ window = TXT_NewWindow(NULL);
+ TXT_SetWindowAction(window, TXT_HORIZ_LEFT, NULL);
+ TXT_SetWindowAction(window, TXT_HORIZ_CENTER,
+ TXT_NewWindowAbortAction(window));
+ TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, NULL);
+
+ label = TXT_NewLabel("Press the new mouse button...");
+
+ TXT_AddWidget(window, label);
+ TXT_SetWidgetAlign(label, TXT_HORIZ_CENTER);
+
+ TXT_SetMouseListener(window, MousePressCallback, mouse_input);
+}
+
+static void TXT_MouseInputSizeCalc(TXT_UNCAST_ARG(mouse_input))
+{
+ TXT_CAST_ARG(txt_mouse_input_t, mouse_input);
+
+ // All mouseinputs are the same size.
+
+ mouse_input->widget.w = MOUSE_INPUT_WIDTH;
+ mouse_input->widget.h = 1;
+}
+
+static void GetMouseButtonDescription(int button, char *buf)
+{
+ switch (button)
+ {
+ case 0:
+ strcpy(buf, "LEFT");
+ break;
+ case 1:
+ strcpy(buf, "RIGHT");
+ break;
+ case 2:
+ strcpy(buf, "MID");
+ break;
+ default:
+ sprintf(buf, "BUTTON #%i", button + 1);
+ break;
+ }
+}
+
+static void TXT_MouseInputDrawer(TXT_UNCAST_ARG(mouse_input), int selected)
+{
+ TXT_CAST_ARG(txt_mouse_input_t, mouse_input);
+ char buf[20];
+ int i;
+
+ if (*mouse_input->variable < 0)
+ {
+ strcpy(buf, "(none)");
+ }
+ else
+ {
+ GetMouseButtonDescription(*mouse_input->variable, buf);
+ }
+
+ TXT_SetWidgetBG(mouse_input, selected);
+ TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
+
+ TXT_DrawString(buf);
+
+ for (i=strlen(buf); i<MOUSE_INPUT_WIDTH; ++i)
+ {
+ TXT_DrawString(" ");
+ }
+}
+
+static void TXT_MouseInputDestructor(TXT_UNCAST_ARG(mouse_input))
+{
+}
+
+static int TXT_MouseInputKeyPress(TXT_UNCAST_ARG(mouse_input), int mouse)
+{
+ TXT_CAST_ARG(txt_mouse_input_t, mouse_input);
+
+ if (mouse == KEY_ENTER)
+ {
+ // Open a window to prompt for the new mouse press
+
+ OpenPromptWindow(mouse_input);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static void TXT_MouseInputMousePress(TXT_UNCAST_ARG(widget), int x, int y, int b)
+{
+ TXT_CAST_ARG(txt_mouse_input_t, widget);
+
+ // Clicking is like pressing enter
+
+ if (b == TXT_MOUSE_LEFT)
+ {
+ TXT_MouseInputKeyPress(widget, KEY_ENTER);
+ }
+}
+
+txt_widget_class_t txt_mouse_input_class =
+{
+ TXT_AlwaysSelectable,
+ TXT_MouseInputSizeCalc,
+ TXT_MouseInputDrawer,
+ TXT_MouseInputKeyPress,
+ TXT_MouseInputDestructor,
+ TXT_MouseInputMousePress,
+ NULL,
+};
+
+txt_mouse_input_t *TXT_NewMouseInput(int *variable)
+{
+ txt_mouse_input_t *mouse_input;
+
+ mouse_input = malloc(sizeof(txt_mouse_input_t));
+
+ TXT_InitWidget(mouse_input, &txt_mouse_input_class);
+ mouse_input->variable = variable;
+
+ return mouse_input;
+}
+
diff --git a/src/setup/txt_mouseinput.h b/src/setup/txt_mouseinput.h
new file mode 100644
index 00000000..ef3ec2aa
--- /dev/null
+++ b/src/setup/txt_mouseinput.h
@@ -0,0 +1,45 @@
+// 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.
+//
+
+#ifndef TXT_MOUSE_INPUT_H
+#define TXT_MOUSE_INPUT_H
+
+typedef struct txt_mouse_input_s txt_mouse_input_t;
+
+#include "txt_widget.h"
+
+//
+// A mouse input is like an input box. When selected, a box pops up
+// allowing a mouse to be selected.
+//
+
+struct txt_mouse_input_s
+{
+ txt_widget_t widget;
+ int *variable;
+ int check_conflicts;
+};
+
+txt_mouse_input_t *TXT_NewMouseInput(int *variable);
+
+#endif /* #ifndef TXT_MOUSE_INPUT_H */
+
+