shithub: choc

Download patch

ref: 391e7466b1efb7cbede4a1c356a210d9e7ee616b
parent: fa328faf056affa216f2f3a8764ca0d56262efe9
parent: 822664b4ff873d462370e9e96a9d91e6066c221d
author: Simon Howard <[email protected]>
date: Mon Jun 13 18:21:37 EDT 2011

Merge from trunk.

Subversion-branch: /branches/raven-branch
Subversion-revision: 2347

--- a/BUGS
+++ /dev/null
@@ -1,13 +1,0 @@
-
-* Sound may not set volumes correctly.
-
-  It is possible that volume of sound effects does not scale properly 
-  with distance from the sound source.  It is also possible that sound 
-  effects are cut off at the wrong distance. This needs further 
-  investigation.
-
-* A small number of Doom bugs are almost impossible to emulate.
-
-  An example of this can be seen in Ledmeister's "Blackbug" demo which 
-  shows a bug that relies on the memory layout of the Doom executable.  
-
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,258 @@
+2011-05-17 23:51:37 fraggle
+	
+	Add dependency for INSTALL generation.
+
+2011-05-17 23:06:22 fraggle
+	
+	Detect chex.deh if it is in the same directory as the IWAD file.
+
+2011-05-14 22:50:46 fraggle
+	
+	Fix display of ENDOOM screen.
+
+2011-05-14 22:47:12 fraggle
+	
+	Fix install of screensaver desktop file.
+
+2011-05-14 22:07:55 fraggle
+	
+	Add freedesktop.org desktop files for chocolate-doom, chocolate-setup
+	(thanks Adrián Chaves Fernández).
+
+2011-05-14 21:11:26 fraggle
+	
+	Convert build system to using the PROGRAM_PREFIX system used on
+	raven-branch.
+
+2011-05-08 19:32:02 fraggle
+	
+	Update NEWS.
+
+2011-05-08 19:29:46 fraggle
+	
+	Allow IWAD files to be double-clicked in the finder to set the IWAD
+	configuration.
+
+2011-05-08 01:31:01 fraggle
+	
+	Shut up compiler warnings.
+
+2011-05-08 00:52:26 fraggle
+	
+	Rework OS X launcher package.  Include documentation files within the
+	application bundle and add a help menu with links.  Rework .dmg
+	generation to generate a file with a "fancy" background image and
+	overall nicer appearance.
+
+2011-04-26 06:49:53 quasar_te
+	
+	Support for Win32 native OPL output when compiled with Microsoft
+	Visual C++.  Confirmed to work with Aureal Vortex AU8830 in Win98SE by
+	GhostlyDeath.
+
+2011-04-24 23:22:11 fraggle
+	
+	Infer -server when -privateserver is specified (thanks Porsche Monty).
+
+2011-04-24 22:39:31 fraggle
+	
+	Add test button to joystick menu in setup tool (thanks Alexandre
+	Xavier).
+
+2011-04-18 23:10:16 fraggle
+	
+	Add test hack for simulating Porsche Monty's scanline emulation (see
+	comment in file).
+
+2011-04-17 18:33:04 fraggle
+	
+	Fix libtextscreen window hotkeys to work when shift is held down /
+	capslock turned on.  Fix a similar problem in-game when typing cheat
+	codes or using menu hotkeys (thanks Alexandre Xavier).
+
+2011-04-17 17:53:47 fraggle
+	
+	Make Final Doom IWAD labels shorter, so they don't make the launch
+	windows grow.
+
+2011-04-11 20:49:45 fraggle
+	
+	Allow the shift key to be held down when changing key/mouse/joystick
+	bindings to prevent bindings to the same key from being cleared
+	(thanks myk).
+
+2011-04-09 01:27:13 fraggle
+	
+	Fix action area minimum width calculation.
+
+2011-04-04 21:30:17 fraggle
+	
+	Close dropdown list popup windows when clicking outside the window.
+
+2011-04-04 21:12:59 fraggle
+	
+	Fix crash.
+
+2011-04-04 21:09:42 fraggle
+	
+	Fix crash.
+
+2011-04-04 21:07:07 fraggle
+	
+	Change the background color when hovering over widgets.
+
+2011-04-04 19:40:28 fraggle
+	
+	Change setup tool default sampling rate to 44100Hz to match the game
+	(thanks Alexandre Xavier).
+
+2011-03-30 20:16:40 fraggle
+	
+	Add a symlink hack to work around the fact that OS X doesn't like
+	paths in MANPATH to contain spaces.
+
+2011-03-30 20:00:51 fraggle
+	
+	On OS X, display a dialog box when exiting with I_Error, like on
+	Windows.
+
+2011-03-29 00:48:31 fraggle
+	
+	Remove the BUGS file as it doesn't really contain any useful
+	information.
+
+2011-03-29 00:39:48 fraggle
+	
+	Add vim modeline for text wrapping to documentation text files.
+
+2011-03-29 00:33:09 fraggle
+	
+	Emulate bug with IDMUS cheat when emulating v1.9 (thanks Alexandre
+	Xavier).
+
+2011-03-28 22:36:00 fraggle
+	
+	Fix OPL MIDI playback when using an empty .mus / .mid file (thanks
+	Alexandre Xavier).
+
+2011-03-28 22:32:14 fraggle
+	
+	Allow .lmp files to be loaded (and demo files to be played back) that
+	have long filenames (thanks blzut3).
+
+2011-03-28 01:24:47 fraggle
+	
+	Fix weapon cycling from the shotgun to the chaingun in Doom 1 (thanks
+	Alexandre Xavier).
+
+2011-03-28 00:45:53 fraggle
+	
+	Scroll faster in reaction to the scroll wheel.
+
+2011-03-28 00:42:00 fraggle
+	
+	Change default sfx/music volume in setup tool to 8, to match the game
+	(thanks Alexandre Xavier).
+
+2011-03-22 21:33:17 fraggle
+	
+	Switch separator to show "screen mode" or "window size" depending on
+	whether fullscreen is turned on or not.
+
+2011-03-22 21:08:04 fraggle
+	
+	Fix scrollbars so that clicks scroll the pane to a location that
+	matches the clicked location.  Interpret mousewheel events so that
+	scroll panes can be scrolled.
+
+2011-03-22 19:49:31 fraggle
+	
+	Reorganise the display settings window.
+
+2011-03-17 22:54:33 fraggle
+	
+	Add back -a option to automake, and remove INSTALL if automake
+	installs it.
+
+2011-03-17 22:43:56 fraggle
+	
+	Fix up placement of display settings window.
+
+2011-03-15 22:41:22 fraggle
+	
+	Fix NEWS entry to list the full name for Alexandre Xavier.
+
+2011-03-10 19:47:14 fraggle
+	
+	Include Unix manpages in MacOS package, and set MANPATH to point to
+	them when opening a terminal window.
+
+2011-03-10 19:45:29 fraggle
+	
+	Minor tweaks to MacOS instructions.
+
+2011-03-10 19:20:10 fraggle
+	
+	Minor tweak to INSTALL instructions.
+
+2011-03-10 19:03:23 fraggle
+	
+	Replace the INSTALL file with a template version that is customized to
+	different platforms.
+
+2011-03-09 19:02:15 fraggle
+	
+	Add null sector dereference emulation code from Prboom+, to fix desync
+	with CLNJ-506.LMP (thanks entryway).
+
+2011-03-09 01:06:07 fraggle
+	
+	Add support for the alternate version of the Final Doom executable
+	that fixes the demo loop crash (thanks Porsche Monty, Enjay).
+
+2011-03-06 20:59:51 fraggle
+	
+	Discard very short sound effects and strip lead-in / lead-out samples
+	that apparently aren't played by Vanilla Doom (thanks Quasar).
+
+2011-03-03 21:41:51 fraggle
+	
+	Fix Visual Studio build (thanks GhostlyDeath).
+
+2011-02-28 20:48:27 fraggle
+	
+	Fix autoadjust of pixel depth in setup tool.
+
+2011-02-05 16:50:28 fraggle
+	
+	Fix bug with libtextscreen where it was not possible to type a '+'
+	(thanks Alexandre Xavier).
+
+2011-01-31 01:25:47 fraggle
+	
+	When large numbers of screen resolutions are detected, increase the
+	number of columns in the mode list to fit them all on-screen.  Remove
+	superfluous left-side spacing from the checkbox and radio button
+	widgets so that the modes can be packed closer together.
+
+2011-01-23 21:42:09 fraggle
+	
+	Fix default joystick buttons in setup tool to match Vanilla (thanks
+	twipley).
+
+2011-01-13 20:34:55 fraggle
+	
+	In configuration files, use the scan code for right shift, not left
+	shift, to match Vanilla (thanks AlexXav).
+
+2011-01-12 23:22:20 fraggle
+	
+	Fix menu navigation when using joystick / joypad (thanks AlexXav).
+
+2011-01-02 18:14:59 fraggle
+	
+	Update NEWS and ChangeLog, bump version number.
+
 2011-01-02 17:45:24 fraggle
 	
 	Remove redundant package version label from top of OS X launcher
--- a/HACKING
+++ b/HACKING
@@ -172,3 +172,5 @@
 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 // 02111-1307, USA.
 
+# vim: tw=70
+
--- a/INSTALL
+++ /dev/null
@@ -1,173 +1,0 @@
-
-Chocolate Doom installation
-===========================
-
-These are instructions for how to install and set up Chocolate Doom
-for play.
-
-Building Chocolate Doom
------------------------
-
-Before you can play Chocolate Doom, you need to compile a binary that
-you can run.  If you are using Windows or Mac OS X, precompiled
-binaries are available on the website for download, and you can skip
-this section.
-
-For compilation, Chocolate Doom requires the following to be installed:
-
- * A C compiler (gcc is recommended)
- * make (GNU make is recommended)
- * LibSDL (see http://www.libsdl.org/)
- * SDL_mixer (see http://www.libsdl.org/projects/SDL_mixer/)
- * SDL_net (see http://www.libsdl.org/projects/SDL_net/)
- * Python (optional)
-
-Follow the standard instructions for installing an autotools-based
-package:
-
- 1. Run './configure' to initialize the package.
- 2. Run 'make' to compile the package.
- 3. Run 'make install' to install the package.
-
-Advanced topics such as cross-compilation are beyond the scope of this
-document.  Please see the GNU autoconf / automake documentation for more
-information.
-
-Obtaining an IWAD file
-----------------------
-
-To play Doom, you need an IWAD file.  This file contains the game data
-that is used in gameplay (graphics, sounds, etc).  The full versions of
-the Doom games are proprietary and need to be bought.  The IWAD file
-has one of the following names:
-
-   doom1.wad                   (Shareware Doom)
-   doom.wad                    (Registered / Ultimate Doom)
-   doom2.wad                   (Doom 2)
-   tnt.wad                     (Final Doom: TNT: Evilution)
-   plutonia.wad                (Final Doom: Plutonia Experiment)
-   chex.wad                    (Chex Quest)
-
-If you don't have a copy of the commercial version, you can download
-the shareware version (extract the file named doom1.wad):
-
- * http://www.doomworld.com/idgames/index.php?id=7053
-   (idstuff/doom/win95/doom95.zip in your nearest /idgames mirror)
-
-If you have a commercial version, obtaining the IWAD file may slightly
-complicated.  The method depends on how you obtained your copy of the
-game:
-
- * There have been several CD-based versions of Doom.  Generally, the
-   IWAD files can be found on the CD and copied off directly.
-
- * The IWAD files might not be directly available on the CD.  Look for
-   a program named "deice.exe".  In the same directory, there should
-   be a single large file with a numbered extension (eg.
-   "resource.1"); to extract this, follow the same instructions as for
-   the floppy disk version (see below).
-
- * If you have the floppy disk version of Doom, first copy the
-   contents of all the floppy disks into a directory together.  You
-   will have several large files with numbered extensions.
-   Concatenate these into a single file, eg.
-
-     (Unix instructions)
-     cat doom_se.1 doom_se.2 doom_se.3 doom_se.4 doom_se.5 > doom_se.exe
-
-     (Windows/DOS instructions)
-     copy doom_se.1+doom_se.2+doom_se.3+doom_se.4+doom_se+5 doom_se.exe
-
-   The resulting file is self-extracting LHA file.  If you have a DOS
-   emulator (such as DOSbox), you can run it to extract the files;
-   alternatively, you can use the Unix LHA tool to extract the
-   archive.
-
- * The Doom games are also available for download on Steam
-   (http://www.steampowered.com/).  To find the IWAD files, look in
-   your Steam directory, under the "steamapps/common" path.
-
-Running the game
-----------------
-
-When you have an IWAD file, install it through one of the following
-methods:
-
- * Under Mac OS X, you can specify the locations of the IWAD files
-   through the graphical launcher program.  Click the "Configure..."
-   button, and then click "Set..." for each IWAD location to choose
-   its location.
-
- * Under Unix, put the file into the /usr/share/games/doom or
-   /usr/local/share/games/doom directories.
-
- * Place it in a directory and set the environment variable DOOMWADDIR
-   to be the path to that directory.
-
- * Install multiple IWADs into separate directories and set the
-   environment variable DOOMWADPATH to be a colon-separated list of
-   directories to search (similar to the Unix PATH environment
-   variable).
-
- * Run Chocolate Doom with the '-iwad' command line parameter to
-   specify the IWAD file to use, eg.
-
-       chocolate-doom -iwad /root/doom2.wad
-
-Playing with Chex Quest
------------------------
-
-Chex Quest is a game based on Doom with some minor modifications that
-was distributed with boxes of Chex cereal in 1997.  It is possible to
-play Chex Quest using Chocolate Doom.  To do this, the following files
-are needed:
-
- * The IWAD file 'chex.wad', from the Chex Quest CD.
-
- * The dehacked patch 'chex.deh', which can be found in the /idgames
-   repository in utils/exe_edit/patches/chexdeh.zip.
-
-Copy these files into a directory together and use the '-iwad' command
-line parameter to specify the Chex Quest IWAD file:
-
-   chocolate-doom -iwad chex.wad
-
-Installing upgrades
--------------------
-
-Chocolate Doom requires a Doom 1.9 IWAD file.  Generally, if you
-install a recent version of Doom you should automatically have a 1.9
-IWAD.  However, if you are installing from a very old CD version or
-from floppy disks, you might find you have an older version.
-
-The most obvious symptom of an out of date IWAD file is that the game
-will exit at the title screen before the demo starts, with the message
-"Demo is from a different game version!".  If this happens, your IWAD
-file is out of date and you need to upgrade.
-
-Id Software released upgrade patches that will update your game to
-1.9.  The following sites have the patches:
-
-  http://www.doomworld.com/files/patches.shtml
-  http://www.doom2.net/doom2/utils.html
-  ftp://ftp.idsoftware.com/idstuff/doom2
-
-As the patches are binary patches that run as DOS executables, you
-will need a DOS emulator (such as DOSBox) to install them.
-
-Music support
--------------
-
-Support for Doom's MIDI music is available through Timidity:
-
-  http://timidity.sourceforge.net/
-
-A good set of patches for Timidity is the eawpats collection, which can
-be found here:
-
-  http://www.doomworld.com/idgames/index.php?id=13928
-  (Doom idgames archive, /sounds/eawpats.zip)
-
-If compiling from source, be sure to compile and install timidity
-before installing SDL_mixer.
-
--- a/Makefile.am
+++ b/Makefile.am
@@ -31,36 +31,32 @@
         codeblocks/setup.cbp                   \
         codeblocks/setup-res.rc
 
-DATA_FILES=                   \
-        data/README           \
-        data/doom.ico         \
-        data/doom8.ico        \
-        data/doom.png         \
-        data/setup.ico        \
-        data/setup8.ico       \
-        data/setup.png        \
-        data/convert-icon
+DOC_FILES=                              \
+        CMDLINE                         \
+        README                          \
+        README.OPL                      \
+        NEWS                            \
+        ChangeLog                       \
+        NOT-BUGS
 
 EXTRA_DIST=                             \
         $(AUX_DIST_GEN)                 \
         $(MSVC_FILES)                   \
         $(CODEBLOCKS_FILES)             \
-        $(DATA_FILES)                   \
+        $(DOC_FILES)                    \
         .lvimrc                         \
-        config.h                        \
-        CMDLINE                         \
         HACKING                         \
-        README.OPL                      \
         TODO                            \
-        BUGS                            \
-        NOT-BUGS                        \
         rpm.spec
 
+docdir=$(prefix)/share/doc/@PACKAGE@
+doc_DATA=$(DOC_FILES)
+
 MAINTAINERCLEANFILES =  $(AUX_DIST_GEN)
 
 docdir=$(prefix)/share/doc/@PACKAGE@
 
-SUBDIRS=wince textscreen opl pcsound src man
+SUBDIRS=wince textscreen opl pcsound data src man
 
 DIST_SUBDIRS=pkg $(SUBDIRS)
 
@@ -70,6 +66,9 @@
 
 CMDLINE : src/
 	./man/docgen -p man/CMDLINE.template src/ > $@
+
+INSTALL : man/INSTALL.template man/simplecpp
+	./man/simplecpp < man/INSTALL.template > $@
 
 endif
 
--- a/NEWS
+++ b/NEWS
@@ -1,22 +1,86 @@
-1.6.0 (2011-??-??):
+1.6.0 (2011-05-17):
 
+     * The instructions in the INSTALL file are now customized for
+       different platforms, and each binary package contains a version
+       with instructions specific to the platform that it is
+       targetting.  This should help to avoid confusion that some
+       users have reported experiencing.
+     * The display settings window in the setup tool has been
+       reorganised to a better arrangement.
+     * It is now possible to load .lmp files (and play back demos)
+       with long filenames (thanks blzut3).
+     * In the setup tool, it is now possible to hold down shift when
+       changing key/mouse/joystick bindings to prevent other bindings
+       to the same key from being cleared (thanks myk).
+     * The joystick menu in the setup tool now has a test button
+       (thanks Alexandre Xavier).
+     * Specifying the -privateserver option implies -server (thanks
+       Porsche Monty).
+     * The Mac OS X .dmg package now has a background and looks generally
+       more polished.
+     * In Mac OS X, it is now possible to simply double click an IWAD
+       file in the Finder to configure its location within the launcher.
+     * Freedesktop.org desktop files are now installed for Doom and
+       the setup tool, which will appear in the main menu on desktop
+       environments such as Gnome and KDE (thanks Adrián Chaves
+       Fernández).
+     * The Chex Quest dehacked patch (chex.deh) will now be detected
+       if it is in the same directory as the IWAD file.
+
     Compatibility:
+     * Added support for the alternate version of the Final Doom
+       executable included in some later versions of the Id Anthology.
+       This version fixed the demo loop crash that occurred with the
+       "original" Final Doom executable.
+
+       This executable can be selected on the command line with
+       -gameversion final2. It has been made the default when playing
+       with the Final Doom IWADs (the original behavior can be
+       selected with -gameversion final).  (thanks Porsche Monty,
+       Enjay).
      * Very short sound effects are not played, to better emulate the
        behavior of DMX in Vanilla Doom (thanks to Quasar for help in
        investigating this).
+     * The null sector dereference emulation code has been imported
+       from Prboom+ - this fixes a desync with CLNJ-506.LMP (thanks
+       entryway).
+     * The IDMUS cheat doesn't work when emulating the v1.9 executable
+       (thanks Alexandre Xavier).
 
     Bugs fixed:
      * Menu navigation when using joystick/joypad (thanks Alexandre
        Xavier).
      * For configuration file value for shift keys, use scan code for
-       right shift, not left shift (thanks AlexXav).
+       right shift, not left shift (thanks Alexandre Xavier).
      * Default joystick buttons for the setup tool now match Vanilla
        (thanks twipley).
      * Visual Studio project files work again (thanks GhostlyDeath).
+     * The default sfx/music volume set by the setup tool is now 8
+       instead of 15, matching the game itself. (thanks Alexandre
+       Xavier).
+     * Weapon cycling from the shotgun to the chaingun in Doom 1 now
+       works properly (thanks Alexandre Xavier).
+     * MIDI playback that locked up when using an empty MUS / MIDI
+       file (thanks Alexandre Xavier).
+     * Default sampling rate used by setup tool changed to 44100Hz, to
+       match the game default (thanks Alexandre Xavier).
+     * Cheat codes and menu hot keys now work when shift is held down
+       or capslock turned on (thanks Alexandre Xavier).
 
     libtextscreen:
+     * The background on GUI controls now lights up when hovering over
+       them, so that it is more obvious what you are selecting.
      * It is now possible to type a '+' in input boxes (thanks
        Alexandre Xavier).
+     * It is possible to use the mouse wheel to scroll through scroll
+       panes.
+     * Clicking on scroll bars now moves the scroll handle to a
+       matching location.
+     * Clicking outside a dropdown list popup window now dismisses the
+       window.
+     * Window hotkeys that are an alphabetical letter now work when
+       shift is held down or capslock turned on (thanks Alexandre
+       Xavier).
 
 1.5.0 (2011-01-02):
 
@@ -622,4 +686,6 @@
 
 0.0.1 (2005-09-07):
     First beta release
+
+# vim: tw=70
 
--- a/NOT-BUGS
+++ b/NOT-BUGS
@@ -31,29 +31,6 @@
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-== Game exits in demo loop when playing Final Doom ==
-
-When playing with the Final Doom IWAD files (tnt.wad, plutonia.wad),
-if you leave the game at the title screen to play through the demo
-loop, it will eventually exit with the following error message:
-
-    W_GetNumForName: demo4 not found!
-
-This is the same behavior as the Vanilla executables that were
-bundled with Final Doom.  When Ultimate Doom was developed, a fourth
-demo was added to the demo loop, and this change was retained in the
-Final Doom version of the executable.  However, the Final Doom IWADs
-do not include a fourth demo, so the game crashes.
-
-One way to work around this problem is to make the game emulate the
-original (pre-Ultimate Doom) v1.9 executable.  To do this, add the
-command line argument "-gameversion 1.9" when running the game.
-However, be aware this version does have some subtle differences that
-will affect the playback of Final Doom demos (lost soul bouncing,
-teleport behavior).
-
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
 == Game exits when accessing the options menu ==
 
 The game may exit with the message "Bad V_DrawPatch" when accessing
@@ -132,4 +109,21 @@
 More information can be found here:
 
     http://rome.ro/lee_killough/editing/visplane.shtml
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+== IDMUS## cheat doesn't work with shareware/registered Doom IWADs ==
+
+The IDMUS cheat allows the in-game music to be changed.  However, in
+the original v1.9 this cheat didn't work properly when playing with
+the Doom 1 (shareware and registered) IWADs.  This bug was fixed in
+the Ultimate Doom and Final Doom executables.
+
+Chocolate Doom emulates this bug.  When playing with the shareware or
+registered Doom IWADs, the IDMUS cheat therefore does not work
+properly.  If you are playing with the Ultimate Doom IWAD, the
+Ultimate Doom executable is emulated by default, so the cheat works
+properly.
+
+# vim: tw=70
 
--- a/README
+++ b/README
@@ -65,10 +65,11 @@
    You are encouraged to sign up and contribute any useful information
    you may have regarding the port!
 
- * Chocolate Doom is not perfect. See the BUGS file for a list of
-   known issues. Because of the nature of the project, you may also
-   encounter Vanilla Doom bugs; these are intentionally present; see
-   the NOT-BUGS file for more information.
+ * Chocolate Doom is not perfect.  Although it aims to accurately
+   emulate Vanilla Doom, some of the behavior of Vanilla Doom can be
+   very difficult to reproduce. Because of the nature of the project,
+   you may also encounter Vanilla Doom bugs; these are intentionally
+   present; see the NOT-BUGS file for more information.
 
    New bug reports can be submitted to the Chocolate Doom bug tracker
    on Sourceforge.  See:
@@ -84,4 +85,6 @@
 
  * Please send any feedback, questions or suggestions to
    [email protected].  Thanks!
+
+# vim: tw=70
 
--- a/README.OPL
+++ b/README.OPL
@@ -105,3 +105,5 @@
 
 There is no native OPL backend for FreeBSD yet.  Sorry!
 
+# vim: tw=70
+
--- a/TODO
+++ b/TODO
@@ -54,3 +54,5 @@
  * Get a better software OPL emulator
  * DMXOPTIONS opl3/phase option support.
 
+# vim: tw=70
+
--- a/autogen.sh
+++ b/autogen.sh
@@ -1,12 +1,26 @@
 #!/bin/sh
 
+if [ -e INSTALL ]; then
+    have_INSTALL=true
+else
+    have_INSTALL=false
+fi
+
 mkdir autotools
 
 aclocal
 autoheader
-automake -a -c
+automake -ac
 autoconf
 automake
+
+# The INSTALL file is autogenerated, so it isn't stored in version control.
+# As the file isn't present, automake's -a option will install generic
+# install instructions.  So remove INSTALL if automake installed one.
+
+if ! $have_INSTALL; then
+    rm -f INSTALL
+fi
 
 ./configure "$@"
 
--- a/codeblocks/config.h
+++ b/codeblocks/config.h
@@ -9,19 +9,22 @@
 #define PACKAGE_NAME "Chocolate Doom"
 
 /* Define to the full name and version of this package. */
-#define PACKAGE_STRING "Chocolate Doom 1.5.0"
+#define PACKAGE_STRING "Chocolate Doom 1.6.0"
 
 /* Define to the one symbol short name of this package. */
 #define PACKAGE_TARNAME "chocolate-doom"
 
 /* Define to the version of this package. */
-#define PACKAGE_VERSION "1.5.0"
+#define PACKAGE_VERSION "1.6.0"
 
+/* Change this when you create your awesome forked version */
+#define PROGRAM_PREFIX "chocolate-"
+
 /* Define to 1 if you have the ANSI C header files. */
 #define STDC_HEADERS 1
 
 /* Version number of package */
-#define VERSION "1.5.0"
+#define VERSION "1.6.0"
 
 /* Define to 1 if your processor stores words with the most significant byte
    first (like Motorola and SPARC, unlike Intel and VAX). */
--- a/codeblocks/game-res.rc
+++ b/codeblocks/game-res.rc
@@ -1,8 +1,8 @@
 1 ICON "../data/doom.ico"
 
 1 VERSIONINFO
-PRODUCTVERSION 1,5,0,0
-FILEVERSION 1,5,0,0
+PRODUCTVERSION 1,6,0,0
+FILEVERSION 1,6,0,0
 FILETYPE 1
 {
  BLOCK "StringFileInfo"
@@ -9,13 +9,13 @@
  {
   BLOCK "040904E4"
   {
-   VALUE "FileVersion", "1.5.0"
-   VALUE "FileDescription", "1.5.0"
+   VALUE "FileVersion", "1.6.0"
+   VALUE "FileDescription", "1.6.0"
    VALUE "InternalName", "Chocolate-Doom"
    VALUE "CompanyName", "Chocolate-Doom"
    VALUE "LegalCopyright", "GNU General Public License"
    VALUE "ProductName", "Chocolate-Doom"
-   VALUE "ProductVersion", "1.5.0"
+   VALUE "ProductVersion", "1.6.0"
   }
  }
 }
--- a/codeblocks/setup-res.rc
+++ b/codeblocks/setup-res.rc
@@ -3,8 +3,8 @@
 CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "setup-manifest.xml"
 
 1 VERSIONINFO
-PRODUCTVERSION 1,5,0,0
-FILEVERSION 1,5,0,0
+PRODUCTVERSION 1,6,0,0
+FILEVERSION 1,6,0,0
 FILETYPE 1
 {
  BLOCK "StringFileInfo"
@@ -11,13 +11,13 @@
  {
   BLOCK "040904E4"
   {
-   VALUE "FileVersion", "1.5.0"
+   VALUE "FileVersion", "1.6.0"
    VALUE "FileDescription", "Chocolate-Doom Setup"
    VALUE "InternalName", "chocolate-setup"
    VALUE "CompanyName", "[email protected]"
    VALUE "LegalCopyright", "GNU General Public License"
    VALUE "ProductName", "Chocolate-Doom Setup"
-   VALUE "ProductVersion", "1.5.0"
+   VALUE "ProductVersion", "1.6.0"
   }
  }
 }
--- a/configure.in
+++ b/configure.in
@@ -1,7 +1,7 @@
-AC_INIT(Chocolate Doom, 1.5.0, [email protected], chocolate-doom)
+AC_INIT(Chocolate Doom, 1.6.0, [email protected], chocolate-doom)
 
 PACKAGE_SHORTDESC="Conservative Doom source port"
-PACKAGE_COPYRIGHT="Copyright (C) 1993-2010"
+PACKAGE_COPYRIGHT="Copyright (C) 1993-2011"
 PACKAGE_LICENSE="GNU General Public License, version 2"
 PACKAGE_MAINTAINER="Simon Howard"
 PACKAGE_URL="http://www.chocolate-doom.org/"
@@ -153,7 +153,9 @@
 pkg/osx/Info-gnustep.plist
 pkg/osx/Info.plist
 rpm.spec
+data/Makefile
 src/Makefile
+src/doom.desktop
 src/doom-screensaver.desktop
 src/doom/Makefile
 src/heretic/Makefile
@@ -161,6 +163,7 @@
 src/resource.rc
 src/setup-res.rc
 src/setup/Makefile
+src/setup/setup.desktop
 src/setup/setup-manifest.xml
 textscreen/Makefile
 textscreen/examples/Makefile
--- /dev/null
+++ b/data/.gitignore
@@ -1,0 +1,4 @@
+Makefile.in
+Makefile
+*-doom.png
+*-setup.png
--- /dev/null
+++ b/data/Makefile.am
@@ -1,0 +1,21 @@
+
+EXTRA_DIST=              \
+        README           \
+        doom.ico         \
+        doom8.ico        \
+        doom.png         \
+        setup.ico        \
+        setup8.ico       \
+        setup.png        \
+        convert-icon
+
+iconsdir = $(prefix)/share/icons
+icons_DATA = @[email protected]   \
+             @[email protected]
+
+@[email protected] : doom.png
+	cp $^ $@
+
+@[email protected] : setup.png
+	cp $^ $@
+
--- /dev/null
+++ b/man/INSTALL.template
@@ -1,0 +1,253 @@
+
+Chocolate Doom installation
+===========================
+
+These are instructions for how to install and set up Chocolate Doom
+for play.
+
+#ifn PRECOMPILED
+Building Chocolate Doom
+-----------------------
+
+Before you can play Chocolate Doom, you need to compile a binary that
+you can run.  For compilation, Chocolate Doom requires the following
+to be installed:
+
+ * A C compiler (gcc is recommended)
+ * make (GNU make is recommended)
+ * LibSDL (see http://www.libsdl.org/)
+ * SDL_mixer (see http://www.libsdl.org/projects/SDL_mixer/)
+ * SDL_net (see http://www.libsdl.org/projects/SDL_net/)
+ * Python (optional)
+
+Follow the standard instructions for installing an autotools-based
+package:
+
+ 1. Run './configure' to initialize the package.
+ 2. Run 'make' to compile the package.
+ 3. Run 'make install' to install the package.
+
+An automated build script is available that installs the necessary
+dependencies and builds the source code automatically.  See the build
+instructions on the website.
+
+Advanced topics such as cross-compilation are beyond the scope of this
+document.  Please see the GNU autoconf / automake documentation for more
+information.
+
+#endif
+Obtaining an IWAD file
+----------------------
+
+To play Doom, you need an IWAD file.  This file contains the game data
+(graphics, sounds, etc).  The full versions of the Doom games are
+proprietary and need to be bought.  The IWAD file has one of the
+following names:
+
+   doom1.wad                   (Shareware Doom)
+   doom.wad                    (Registered / Ultimate Doom)
+   doom2.wad                   (Doom 2)
+   tnt.wad                     (Final Doom: TNT: Evilution)
+   plutonia.wad                (Final Doom: Plutonia Experiment)
+   chex.wad                    (Chex Quest)
+
+If you don't have a copy of a commercial version, you can download
+the shareware version (extract the file named doom1.wad):
+
+ * http://www.doomworld.com/idgames/index.php?id=7053
+   (idstuff/doom/win95/doom95.zip in your nearest /idgames mirror)
+
+If you have a commercial version, obtaining the IWAD file is usually
+straightforward.  The method depends on how you obtained your copy of
+the game:
+
+#if _WIN32
+ * The Doom games are available to buy for download on Steam
+   (http://www.steampowered.com/).  Chocolate Doom will autodetect
+   IWADs installed by Steam and you do not need to do anything.
+#else
+ * The Doom games are available to buy for download on Steam
+   (http://www.steampowered.com/).  To find the IWAD files on a
+   Windows system, look in the Steam directory (usually within
+   "Program Files"), under the "steamapps/common" path.
+#endif
+
+ * There have been several CD-based versions of Doom.  Generally, the
+   IWAD files can be found on the CD and copied off directly.
+
+#if _WIN32
+ * If the IWAD files are not directly available on the CD, or you have
+   a floppy disk version, you will need to run the install program to
+   install the game to your hard disk.  As the installer is DOS-based,
+   you may not be able to do this on 64-bit versions of Windows.  In
+   this case, the best suggestion is to use a DOS emulator (such as
+   DOSbox) to run the installer.
+#else
+ * If the IWAD files are not directly available on the CD, or you have
+   a floppy disk version, installation is more difficult.  The best
+   suggestion is to use a DOS emulator (such as DOSbox) to run the
+   installer.
+#endif
+
+ * As an alternative to using an emulator, it is possible to extract
+   the files manually.  On the install disk(s), you will find several
+   files with numbered extensions (with CD versions there may be a
+   single large file with the extension .1, eg. "resource.1").
+
+   From the command line it is possible to combine these files into a
+   single large file, using a command similar to the following:
+
+#if _WIN32
+     copy doom_se.1+doom_se.2+doom_se.3+doom_se.4+doom_se.5 doom_se.lha
+#else
+     cat doom_se.1 doom_se.2 doom_se.3 doom_se.4 doom_se.5 > doom_se.lha
+#endif
+
+   The resulting file is an LHA archive file, and it can be extracted
+   using an LHA archive tool (there is one available for almost every
+   operating system).
+
+Running the game
+----------------
+
+#if __MACOSX__
+Once you have an IWAD file, you can specify its location within the
+graphical launcher program.  Click the "Configure..." button, and then
+click "Set..." for each IWAD to choose its location.  From the main
+launcher dialog you can then choose which game you want to play and
+click the "Launch" button to start the game.
+
+If you are an advanced user and like to run Doom from the command
+line, you can use the "Command Prompt..." menu item to open a Terminal
+window.  The DOOMWADPATH environment variable is preconfigured
+to point to the locations of the IWAD files set within the launcher.
+You can launch the game with a specific IWAD file by typing, for
+example:
+
+    chocolate-doom -iwad tnt.wad
+#else
+Chocolate Doom needs to know where to find your IWAD file. To do this,
+do one of the following:
+
+#if _WIN32
+ * Within Explorer, simply place the IWAD file in the same folder as
+   the Chocolate Doom files, and double-click chocolate-doom.exe.
+
+ * Run Chocolate Doom from the command prompt with the '-iwad' command
+   line parameter to specify the IWAD file to use, eg.
+
+       chocolate-doom -iwad c:\games\doom2.wad
+
+ * Set the environment variable DOOMWADDIR to the location of a
+   directory containing your IWAD files.
+
+ * If you have multiple IWADs in different directories, set the
+   environment variable DOOMWADPATH to be a semicolon-separated list
+   of directories to search (similar to the PATH environment
+   variable).
+#else
+ * Run Chocolate Doom from the Unix console with the '-iwad' command
+   line parameter to specify the IWAD file to use, eg.
+
+       chocolate-doom -iwad /root/doom2.wad
+
+ * Put the file into one of the following directories:
+
+     /usr/share/games/doom
+     /usr/local/share/games/doom
+
+ * Set the environment variable DOOMWADDIR to specify the path to a
+   directory containing your IWAD files.
+
+ * If you have multiple IWADs in different directories, set the
+   environment variable DOOMWADPATH to be a colon-separated list of
+   directories to search (similar to the Unix PATH environment
+   variable).
+#endif
+#endif
+
+Playing with Chex Quest
+-----------------------
+
+Chex Quest is a game based on Doom with some minor modifications that
+was distributed with boxes of Chex cereal in 1997.  It is possible to
+play Chex Quest using Chocolate Doom.  To do this, the following files
+are needed:
+
+ * The IWAD file 'chex.wad', from the Chex Quest CD.
+
+ * The dehacked patch 'chex.deh', which can be found here:
+   http://www.doomworld.com/idgames/?id=15420
+   (utils/exe_edit/patches/chexdeh.zip in your nearest /idgames mirror)
+
+Copy these files into a directory together and use the '-iwad' command
+line parameter to specify the Chex Quest IWAD file:
+
+   chocolate-doom -iwad chex.wad
+
+Installing upgrades
+-------------------
+
+Chocolate Doom requires a version 1.9 IWAD file.  Generally, if you
+install a recent version of Doom you should have a version 1.9 IWAD.
+However, if you are installing from a very old CD version or from
+floppy disks, you might find you have an older version.
+
+The most obvious symptom of an out of date IWAD file is that the game
+will exit at the title screen before the demo starts, with the message
+"Demo is from a different game version!".  If this happens, your IWAD
+file is out of date and you need to upgrade.
+
+Id Software released upgrade patches that will update your game to
+version 1.9.  The following sites have the patches:
+
+  http://www.doomworld.com/files/patches.shtml
+  http://www.doom2.net/doom2/utils.html
+  ftp://ftp.idsoftware.com/idstuff/doom2
+
+#if _WIN32
+As the patches are binary patches that run as DOS executables, on
+recent 64-bit versions of Windows you will need to use a DOS emulator
+(such as DOSBox) to run them.
+#else
+As the patches are binary patches that run as DOS executables, you
+will need to use a DOS emulator (such as DOSBox) to run them.
+#endif
+
+Music support
+-------------
+
+Chocolate Doom includes OPL emulation code that accurately reproduces
+the way that the in-game music sounded under DOS when using an
+Adlib/Soundblaster card.  This is, however, not to everyone's taste.
+
+#if _WIN32
+Better quality MIDI playback is possible by using Windows' native
+MIDI synthesizer that is part of the operating system.  Select "Native
+MIDI" within the sound dialog in the setup tool.
+
+#endif
+#if __MACOSX__
+High quality MIDI playback is possible by using Mac OS X's native MIDI
+synthesizer that is part of the operating system. Select "Native MIDI"
+within the sound dialog in the setup tool.
+
+#endif
+As an alternative it is possible to use Timidity for high quality MIDI
+playback:
+
+  http://timidity.sourceforge.net/
+
+A good set of patches for Timidity is the eawpats collection, which can
+be found here:
+
+  http://www.doomworld.com/idgames/index.php?id=13928
+  (Doom idgames archive, /sounds/eawpats.zip)
+
+#ifn PRECOMPILED
+When compiling from source, be sure to compile and install timidity
+before installing SDL_mixer.
+#endif
+
+# vim: tw=70
+
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -1,6 +1,8 @@
 
 MANPAGE_GEN_FILES=manpage.template docgen default.cfg.template extra.cfg.template
 
+docdir=$(prefix)/share/doc/@PACKAGE@
+
 if HAVE_PYTHON
 
 man_MANS=chocolate-doom.6      \
@@ -9,6 +11,8 @@
          default.cfg.5         \
          $(PACKAGE).cfg.5
 
+nodist_doc_DATA=INSTALL
+
 chocolate-doom.6: ../src $(MANPAGE_GEN_FILES)
 	./docgen -m manpage.template ../src > $@
 
@@ -18,9 +22,14 @@
 $(PACKAGE).cfg.5: ../src extra.cfg.template
 	./docgen -m extra.cfg.template -c $(PACKAGE).cfg ../src > $@
 
+INSTALL: INSTALL.template
+	./simplecpp -DPRECOMPILED < INSTALL.template > $@
+
 endif
 
 EXTRA_DIST = $(man_MANS) $(MANPAGE_GEN_FILES)        \
              wikipages                               \
-             CMDLINE.template
+             CMDLINE.template                        \
+             INSTALL.template                        \
+             simplecpp
 
--- /dev/null
+++ b/man/simplecpp
@@ -1,0 +1,211 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+# Contributors to the Freedoom project.  All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#  * Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+#  * Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#  * Neither the name of the freedoom project nor the names of its
+#    contributors may be used to endorse or promote products derived from
+#    this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+#
+# simple cpp-style preprocessor
+#
+# Understands:
+#
+# #define NAME
+#
+# Set an option
+# You can use -D on the command line too
+#
+# #undef NAME
+#
+# Unset an option if it is set
+#
+# #if .. #endif / #ifdef .. #endif
+#
+# Specify a list of options set, eg #ifdef DOOM2 || ULTDOOM || SHAREWARE
+# The block is only displayed if one of the options is set
+#
+# #ifn .. #endif / #ifndef .. #endif
+#
+# Similarly specify a list of options
+# The block is displayed if none of the options are set
+#
+# #include "filename"
+#
+# include the contents of a file
+
+import sys
+import re
+
+debug = False
+defines = {}
+
+command_re = re.compile("\#(\w+)(\s+(.*))?")
+include_re = re.compile("\s*\"(.*)\"\s*")
+
+def debug_msg(message):
+	if debug:
+		sys.stderr.write(message)
+
+# Parse command line options
+
+def parse_cmdline():
+	for arg in sys.argv[1:]:
+		if arg.startswith("-D"):
+			name = arg[2:]
+			defines[name] = True
+
+def parse_stream(stream):
+	result = read_block(stream, False)
+
+	if result is not None:
+		raise Exception("Mismatched #if in '%s'" % stream.name)
+
+def parse_file(filename):
+	f = open(filename)
+
+	try:
+		parse_stream(f)
+	finally:
+		f.close()
+
+# #include
+
+def cmd_include(arg):
+	# Extract the filename
+
+	match = include_re.match(arg)
+
+	if not match:
+		raise Exception("Invalid 'include' command")
+
+	filename = match.group(1)
+
+	# Open the file and process it
+
+	parse_file(filename)
+
+# #define
+
+def cmd_define(arg):
+	defines[arg] = True
+
+# #undef
+
+def cmd_undef(arg):
+	if arg in defines:
+		del defines[arg]
+
+# #ifdef/#ifndef
+
+def cmd_ifdef(arg, command, stream, ignore):
+
+	# Get the define name
+	name = arg.strip()
+
+	debug_msg("%s %s >\n" % (command, arg))
+
+	# Should we ignore the contents of this block?
+
+	sub_ignore = (name not in defines)
+
+	if "n" in command:
+		sub_ignore = not sub_ignore
+
+	# Parse the block
+
+	result = read_block(stream, ignore or sub_ignore)
+
+	debug_msg("%s %s < (%s)\n" % (command, arg, result))
+
+	# There may be a second "else" block to parse:
+
+	if result == "else":
+		debug_msg("%s %s else >\n" % (command, arg))
+		result = read_block(stream, ignore or (not sub_ignore))
+		debug_msg("%s %s else < (%s)\n" % (command, arg, result))
+
+	# Should end in an endif:
+
+	if result != "endif":
+		raise Exception("'if' block did not end in an 'endif'")
+
+commands = {
+	"include" : cmd_include,
+	"define"  : cmd_define,
+	"undef"   : cmd_undef,
+	"if"      : cmd_ifdef,
+	"ifdef"   : cmd_ifdef,
+	"ifn"     : cmd_ifdef,
+	"ifndef"  : cmd_ifdef,
+}
+
+# Recursive block reading function
+# if 'ignore' argument is 1, contents are ignored
+
+def read_block(stream, ignore):
+
+	for line in stream:
+
+		# Remove newline
+
+		line = line[0:-1]
+
+		# Check if this line has a command
+
+		match = command_re.match(line)
+
+		if match:
+			command = match.group(1)
+			arg = match.group(3)
+
+			if command == "else" or command == "endif":
+				return command
+			elif command not in commands:
+				raise Exception("Unknown command: '%s'" % \
+						command)
+
+			# Get the callback function.
+
+			func = commands[command]
+
+			# Invoke the callback function. #ifdef commands
+			# are a special case and need extra arguments.
+			# Other commands are only executed if we are not
+			# ignoring this block.
+
+			if func == cmd_ifdef:
+				cmd_ifdef(arg, command=command,
+					       stream=stream,
+					       ignore=ignore)
+			elif not ignore:
+				func(arg)
+		else:
+			if not ignore:
+				print(line)
+
+parse_cmdline()
+parse_stream(sys.stdin)
+
--- a/msvc/config.h
+++ b/msvc/config.h
@@ -11,16 +11,19 @@
 #define PACKAGE_NAME "Chocolate Doom"
 
 /* Define to the full name and version of this package. */
-#define PACKAGE_STRING "Chocolate Doom 1.5.0"
+#define PACKAGE_STRING "Chocolate Doom 1.6.0"
 
 /* Define to the one symbol short name of this package. */
 #define PACKAGE_TARNAME "chocolate-doom"
 
 /* Define to the version of this package. */
-#define PACKAGE_VERSION "1.5.0"
+#define PACKAGE_VERSION "1.6.0"
 
+/* Change this when you create your awesome forked version */
+#define PROGRAM_PREFIX "chocolate-"
+
 /* Version number of package */
-#define VERSION "1.5.0"
+#define VERSION "1.6.0"
 
 /* Define to 1 if your processor stores words with the most significant byte
    first (like Motorola and SPARC, unlike Intel and VAX). */
--- a/msvc/win32.rc
+++ b/msvc/win32.rc
@@ -32,8 +32,8 @@
 #endif
 
 1 VERSIONINFO
-PRODUCTVERSION 1,5,0,0
-FILEVERSION 1,5,0,0
+PRODUCTVERSION 1,6,0,0
+FILEVERSION 1,6,0,0
 FILETYPE 1
 BEGIN
 	BLOCK "StringFileInfo"
@@ -40,13 +40,13 @@
 	BEGIN
 		BLOCK "040904E4"
 		BEGIN
-			VALUE "FileVersion", "1.5.0"
-			VALUE "FileDescription", "Chocolate Doom 1.5.0"
+			VALUE "FileVersion", "1.6.0"
+			VALUE "FileDescription", "Chocolate Doom 1.6.0"
 			VALUE "InternalName", "chocolate-doom"
 			VALUE "CompanyName", "[email protected]"
 			VALUE "LegalCopyright", "GNU General Public License"
 			VALUE "ProductName", "Chocolate Doom"
-			VALUE "ProductVersion", "1.5.0"
+			VALUE "ProductVersion", "1.6.0"
 		END
 	END
 END
--- a/opl/opl_win32.c
+++ b/opl/opl_win32.c
@@ -72,8 +72,35 @@
     );
 }
 
-// TODO: MSVC version
-// #elif defined(_MSC_VER) && defined(_M_IX6) ...
+// haleyjd 20110417: MSVC version
+#elif defined(_MSC_VER) && defined(_M_IX86)
+
+static unsigned int OPL_Win32_PortRead(opl_port_t port)
+{
+    unsigned char result;
+    opl_port_t dst_port = opl_port_base + port;
+    
+    __asm    
+    {
+        mov edx, dword ptr [dst_port]
+        in al, dx
+        mov byte ptr [result], al
+    }
+    
+    return result;
+}
+
+static void OPL_Win32_PortWrite(opl_port_t port, unsigned int value)
+{
+    opl_port_t dst_port = opl_port_base + port;
+    
+    __asm    
+    {
+        mov edx, dword ptr [dst_port]
+        mov al, byte ptr [value]
+        out dx, al
+    }
+}
 
 #else
 
--- a/pkg/Makefile.am
+++ b/pkg/Makefile.am
@@ -7,10 +7,13 @@
 osx/Resources/wadfile.png                                               \
 osx/Resources/launcher.nib/designable.nib                               \
 osx/Resources/launcher.nib/keyedobjects.nib                             \
+osx/disk/dir.DS_Store                                                   \
+osx/disk/background.png                                                 \
 osx/GNUmakefile                                                         \
 osx/Info.plist.in       osx/Info-gnustep.plist.in                       \
 osx/PkgInfo                                                             \
 osx/cp-with-libs                                                        \
+osx/dmgfix                                                              \
 osx/main.m                                                              \
 osx/AppController.m     osx/AppController.h                             \
 osx/Execute.m           osx/Execute.h                                   \
--- a/pkg/config.make.in
+++ b/pkg/config.make.in
@@ -22,9 +22,7 @@
 DOC_FILES = README       \
             COPYING      \
             ChangeLog    \
-            INSTALL      \
             NEWS         \
-            BUGS         \
             NOT-BUGS     \
             CMDLINE      \
             TODO
--- a/pkg/osx/AppController.m
+++ b/pkg/osx/AppController.m
@@ -83,6 +83,14 @@
 {
     NSString *extension;
 
+    // This may be an IWAD.  If so, add it to the IWAD configuration;
+    // don't add it like a PWAD.
+
+    if ([self->launcherManager addIWADPath: fileName])
+    {
+        return YES;
+    }
+
     // If this is the first file added, clear out the existing
     // command line.  This allows us to select multiple files
     // in the finder and open them all together (for TCs, etc).
--- a/pkg/osx/Execute.h
+++ b/pkg/osx/Execute.h
@@ -26,6 +26,7 @@
 void SetProgramLocation(const char *path);
 void ExecuteProgram(const char *executable, const char *iwad, const char *args);
 void OpenTerminalWindow(const char *doomwadpath);
+void OpenDocumentation(const char *filename);
 
 #endif /* #ifndef LAUNCHER_EXECUTE_H */
 
--- a/pkg/osx/Execute.m
+++ b/pkg/osx/Execute.m
@@ -175,6 +175,19 @@
     fprintf(stream, "#!/bin/sh\n");
     //fprintf(stream, "set -x\n");
     fprintf(stream, "PATH=\"%s:$PATH\"\n", executable_path);
+
+    // MANPATH is set to point to the directory within the bundle that
+    // contains the Unix manpages.  However, the bundle name or path to
+    // it can contain a space, and OS X doesn't like this!  As a
+    // workaround, create a symlink in /tmp to point to the real directory,
+    // and put *this* in MANPATH.
+
+    fprintf(stream, "rm -f \"/tmp/%s.man\"\n", PACKAGE_TARNAME);
+    fprintf(stream, "ln -s \"%s/man\" \"/tmp/%s.man\"\n",
+                    executable_path, PACKAGE_TARNAME);
+    fprintf(stream, "MANPATH=\"/tmp/%s.man:$(manpath)\"\n", PACKAGE_TARNAME);
+    fprintf(stream, "export MANPATH\n");
+
     fprintf(stream, "DOOMWADPATH=\"%s\"\n", doomwadpath);
     fprintf(stream, "export DOOMWADPATH\n");
     fprintf(stream, "rm -f \"%s\"\n", TEMP_SCRIPT);
@@ -204,5 +217,15 @@
 
     [[NSWorkspace sharedWorkspace] openFile: @TEMP_SCRIPT
                                    withApplication: @"Terminal"];
+}
+
+void OpenDocumentation(const char *filename)
+{
+    NSString *path;
+
+    path = [NSString stringWithFormat: @"%s/Documentation/%s",
+                     executable_path, filename];
+
+    [[NSWorkspace sharedWorkspace] openFile: path];
 }
 
--- a/pkg/osx/GNUmakefile
+++ b/pkg/osx/GNUmakefile
@@ -20,10 +20,18 @@
 
 # DMG file containing package:
 
-$(DMG) : $(STAGING_DIR)
+$(DMG) : tmp.dmg
 	rm -f $@
-	hdiutil create -volname "$(PACKAGE_STRING)" -srcdir $(STAGING_DIR) $@
+	./dmgfix "$(realpath tmp.dmg)" "$(PACKAGE_STRING)" "$(PACKAGE_NAME).app"
+	hdiutil convert -format UDZO -o $@ tmp.dmg
+	rm -f tmp.dmg
 
+tmp.dmg : $(STAGING_DIR)
+	rm -f $@
+	hdiutil makehybrid -hfs -hfs-volume-name "$(PACKAGE_STRING)"     \
+	                   -hfs-openfolder $(STAGING_DIR) $(STAGING_DIR) \
+			   -o tmp.dmg
+
 endif
 
 # Staging dir build for package:
@@ -46,12 +54,13 @@
 SRC_INFO_PLIST=Info.plist
 endif
 
+APP_DOC_DIR=$(APP_BIN_DIR)/Documentation
+APP_DOC_RELDIR=$(patsubst $(STAGING_DIR)/%,%,$(APP_DOC_DIR))
+
 $(STAGING_DIR): launcher $(TOPLEVEL_DOCS)
 	rm -rf $(STAGING_DIR)
 	mkdir $(STAGING_DIR)
 
-	cp $(TOPLEVEL_DOCS) "$(STAGING_DIR)"
-
 	mkdir -p "$(APP_TOP_DIR)"
 	cp -R Resources "$(APP_TOP_DIR)"
 	cp PkgInfo "$(APP_TOP_DIR)"
@@ -59,6 +68,13 @@
 
 	mkdir -p "$(APP_BIN_DIR)"
 
+	mkdir -p "$(APP_DOC_DIR)"
+	cp $(TOPLEVEL_DOCS) "$(APP_DOC_DIR)"
+
+	ln -s "$(APP_DOC_RELDIR)/COPYING" "$(STAGING_DIR)/Software License"
+	ln -s "$(APP_DOC_RELDIR)/README" "$(STAGING_DIR)/README"
+	ln -s /Applications "$(STAGING_DIR)"
+
 	cp launcher "$(APP_BIN_DIR)"
 	$(STRIP) "$(APP_BIN_DIR)/launcher"
 
@@ -71,7 +87,17 @@
 	./cp-with-libs $(TOPLEVEL)/src/$(PROGRAM_PREFIX)setup "$(APP_BIN_DIR)"
 	$(STRIP) "$(APP_BIN_DIR)/$(PROGRAM_PREFIX)setup"
 
+	$(TOPLEVEL)/man/simplecpp -DPRECOMPILED -D__MACOSX__  \
+	             < $(TOPLEVEL)/man/INSTALL.template       \
+	             > "$(APP_DOC_DIR)/INSTALL"
+
 	find $(STAGING_DIR) -name .svn -delete -exec rm -rf {} \; || true
+
+	mkdir -p "$(APP_BIN_DIR)/man/man5" "$(APP_BIN_DIR)/man/man6"
+	cp $(TOPLEVEL)/man/*.5 "$(APP_BIN_DIR)/man/man5"
+	cp $(TOPLEVEL)/man/*.6 "$(APP_BIN_DIR)/man/man6"
+	cp disk/dir.DS_Store $(STAGING_DIR)/.DS_Store
+	cp disk/background.png $(STAGING_DIR)/background.png
 
 clean : launcher_clean
 	rm -f $(DMG)
--- a/pkg/osx/IWADController.h
+++ b/pkg/osx/IWADController.h
@@ -51,6 +51,7 @@
 - (char *) doomWadPath;
 - (void) setEnvironment;
 - (const char *) getGameName;
+- (BOOL) addIWADPath: (NSString *) path;
 
 @end
 
--- a/pkg/osx/IWADController.m
+++ b/pkg/osx/IWADController.m
@@ -376,5 +376,44 @@
     //free(env);
 }
 
+// Examine a path to a WAD and determine whether it is an IWAD file.
+// If so, it is added to the IWAD configuration, and true is returned.
+
+- (BOOL) addIWADPath: (NSString *) path
+{
+    IWADLocation *iwadList[NUM_IWAD_TYPES];
+    NSArray *pathComponents;
+    NSString *filename;
+    unsigned int i;
+
+    [self getIWADList: iwadList];
+
+    // Find an IWAD file that matches the filename in the path that we
+    // have been given.
+
+    pathComponents = [path pathComponents];
+    filename = [pathComponents objectAtIndex: [pathComponents count] - 1];
+
+    for (i = 0; i < NUM_IWAD_TYPES; ++i)
+    {
+        if ([filename caseInsensitiveCompare: IWADFilenames[i]] == 0)
+        {
+            // Configure this IWAD.
+
+            [iwadList[i] setLocation: path];
+
+            // Rebuild dropdown list and select the new IWAD.
+
+            [self setDropdownList];
+            [self->iwadSelector selectItemWithTitle:IWADLabels[i]];
+            return YES;
+        }
+    }
+
+    // No IWAD found with this name.
+
+    return NO;
+}
+
 @end
 
--- a/pkg/osx/LauncherManager.h
+++ b/pkg/osx/LauncherManager.h
@@ -41,9 +41,16 @@
 - (void) runSetup: (id)sender;
 - (void) awakeFromNib;
 - (void) clearCommandLine;
+- (BOOL) addIWADPath: (NSString *) path;
 - (void) addFileToCommandLine: (NSString *) fileName
          forArgument: (NSString *) args;
 - (void) openTerminal: (id) sender;
+
+- (void) openREADME: (id) sender;
+- (void) openINSTALL: (id) sender;
+- (void) openCMDLINE: (id) sender;
+- (void) openCOPYING: (id) sender;
+- (void) openDocumentation: (id) sender;
 
 @end
 
--- a/pkg/osx/LauncherManager.m
+++ b/pkg/osx/LauncherManager.m
@@ -343,6 +343,31 @@
     free(doomwadpath);
 }
 
+- (void) openREADME: (id) sender
+{
+    OpenDocumentation("README");
+}
+
+- (void) openINSTALL: (id) sender
+{
+    OpenDocumentation("INSTALL");
+}
+
+- (void) openCMDLINE: (id) sender
+{
+    OpenDocumentation("CMDLINE");
+}
+
+- (void) openCOPYING: (id) sender
+{
+    OpenDocumentation("COPYING");
+}
+
+- (void) openDocumentation: (id) sender
+{
+    OpenDocumentation("");
+}
+
 - (void) awakeFromNib
 {
     [self->launcherWindow setTitle: @PACKAGE_NAME " Launcher"];
@@ -349,6 +374,11 @@
     [self->launcherWindow center];
     [self->launcherWindow setDefaultButtonCell: [self->launchButton cell]];
     [self setConfig];
+}
+
+- (BOOL) addIWADPath: (NSString *) path
+{
+    return [self->iwadController addIWADPath: path];
 }
 
 @end
--- a/pkg/osx/Resources/launcher.nib/designable.nib
+++ b/pkg/osx/Resources/launcher.nib/designable.nib
@@ -2,19 +2,19 @@
 <archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
 	<data>
 		<int key="IBDocument.SystemTarget">1060</int>
-		<string key="IBDocument.SystemVersion">10F569</string>
-		<string key="IBDocument.InterfaceBuilderVersion">823</string>
-		<string key="IBDocument.AppKitVersion">1038.29</string>
+		<string key="IBDocument.SystemVersion">10J869</string>
+		<string key="IBDocument.InterfaceBuilderVersion">851</string>
+		<string key="IBDocument.AppKitVersion">1038.35</string>
 		<string key="IBDocument.HIToolboxVersion">461.00</string>
 		<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
 			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
-			<string key="NS.object.0">823</string>
+			<string key="NS.object.0">851</string>
 		</object>
 		<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
 			<bool key="EncodedWithXMLCoder">YES</bool>
-			<integer value="29"/>
+			<integer value="370"/>
 			<integer value="2"/>
-			<integer value="227"/>
+			<integer value="228"/>
 		</object>
 		<object class="NSArray" key="IBDocument.PluginDependencies">
 			<bool key="EncodedWithXMLCoder">YES</bool>
@@ -600,6 +600,73 @@
 							<string key="NSName">_NSWindowsMenu</string>
 						</object>
 					</object>
+					<object class="NSMenuItem" id="391919375">
+						<reference key="NSMenu" ref="624798014"/>
+						<string key="NSTitle">Help</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="819247708"/>
+						<reference key="NSMixedImage" ref="94574589"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="733442466">
+							<string key="NSTitle">Help</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="860531190">
+									<reference key="NSMenu" ref="733442466"/>
+									<string key="NSTitle">Introduction</string>
+									<string key="NSKeyEquiv">?</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="378926680">
+									<reference key="NSMenu" ref="733442466"/>
+									<string key="NSTitle">Set up guide</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="784926086">
+									<reference key="NSMenu" ref="733442466"/>
+									<string key="NSTitle">Command line reference</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="913959081">
+									<reference key="NSMenu" ref="733442466"/>
+									<string key="NSTitle">More documentation...</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="258703436">
+									<reference key="NSMenu" ref="733442466"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="590365178">
+									<reference key="NSMenu" ref="733442466"/>
+									<string key="NSTitle">Software license</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+							</object>
+							<string key="NSName">_NSHelpMenu</string>
+						</object>
+					</object>
 				</object>
 				<string key="NSName">_NSMainMenu</string>
 			</object>
@@ -921,6 +988,26 @@
 								<int key="NSPeriodicInterval">25</int>
 							</object>
 						</object>
+						<object class="NSButton" id="811205099">
+							<reference key="NSNextResponder" ref="145141922"/>
+							<int key="NSvFlags">268</int>
+							<string key="NSFrame">{{17, 16}, {25, 25}}</string>
+							<reference key="NSSuperview" ref="145141922"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSButtonCell" key="NSCell" id="33467307">
+								<int key="NSCellFlags">67239424</int>
+								<int key="NSCellFlags2">134217728</int>
+								<string key="NSContents"/>
+								<reference key="NSSupport" ref="407649812"/>
+								<reference key="NSControlView" ref="811205099"/>
+								<int key="NSButtonFlags">-2038415105</int>
+								<int key="NSButtonFlags2">161</int>
+								<string key="NSAlternateContents"/>
+								<string key="NSKeyEquivalent"/>
+								<int key="NSPeriodicDelay">200</int>
+								<int key="NSPeriodicInterval">25</int>
+							</object>
+						</object>
 					</object>
 					<string key="NSFrameSize">{480, 316}</string>
 					<reference key="NSSuperview"/>
@@ -1312,6 +1399,54 @@
 					</object>
 					<int key="connectionID">322</int>
 				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">openINSTALL:</string>
+						<reference key="source" ref="590266459"/>
+						<reference key="destination" ref="378926680"/>
+					</object>
+					<int key="connectionID">374</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">openCMDLINE:</string>
+						<reference key="source" ref="590266459"/>
+						<reference key="destination" ref="784926086"/>
+					</object>
+					<int key="connectionID">376</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">openDocumentation:</string>
+						<reference key="source" ref="590266459"/>
+						<reference key="destination" ref="913959081"/>
+					</object>
+					<int key="connectionID">378</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">openCOPYING:</string>
+						<reference key="source" ref="590266459"/>
+						<reference key="destination" ref="590365178"/>
+					</object>
+					<int key="connectionID">381</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">openREADME:</string>
+						<reference key="source" ref="590266459"/>
+						<reference key="destination" ref="860531190"/>
+					</object>
+					<int key="connectionID">382</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">openINSTALL:</string>
+						<reference key="source" ref="590266459"/>
+						<reference key="destination" ref="811205099"/>
+					</object>
+					<int key="connectionID">385</int>
+				</object>
 			</object>
 			<object class="IBMutableOrderedSet" key="objectRecords">
 				<object class="NSArray" key="orderedObjects">
@@ -1442,7 +1577,8 @@
 							<reference ref="419477060"/>
 							<reference ref="231654028"/>
 							<reference ref="729612487"/>
-						</object>
+							<reference ref="391919375"/>
+						</object>
 						<reference key="parent" ref="0"/>
 						<string key="objectName">MainMenu</string>
 					</object>
@@ -1693,6 +1829,7 @@
 							<reference ref="625273251"/>
 							<reference ref="680095551"/>
 							<reference ref="658359713"/>
+							<reference ref="811205099"/>
 						</object>
 						<reference key="parent" ref="193084417"/>
 					</object>
@@ -2035,6 +2172,73 @@
 						<reference key="object" ref="366010945"/>
 						<reference key="parent" ref="0"/>
 					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">369</int>
+						<reference key="object" ref="391919375"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="733442466"/>
+						</object>
+						<reference key="parent" ref="624798014"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">370</int>
+						<reference key="object" ref="733442466"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="860531190"/>
+							<reference ref="378926680"/>
+							<reference ref="784926086"/>
+							<reference ref="913959081"/>
+							<reference ref="258703436"/>
+							<reference ref="590365178"/>
+						</object>
+						<reference key="parent" ref="391919375"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">371</int>
+						<reference key="object" ref="860531190"/>
+						<reference key="parent" ref="733442466"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">373</int>
+						<reference key="object" ref="378926680"/>
+						<reference key="parent" ref="733442466"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">375</int>
+						<reference key="object" ref="784926086"/>
+						<reference key="parent" ref="733442466"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">377</int>
+						<reference key="object" ref="913959081"/>
+						<reference key="parent" ref="733442466"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">379</int>
+						<reference key="object" ref="258703436"/>
+						<reference key="parent" ref="733442466"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">380</int>
+						<reference key="object" ref="590365178"/>
+						<reference key="parent" ref="733442466"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">383</int>
+						<reference key="object" ref="811205099"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="33467307"/>
+						</object>
+						<reference key="parent" ref="145141922"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">384</int>
+						<reference key="object" ref="33467307"/>
+						<reference key="parent" ref="811205099"/>
+					</object>
 				</object>
 			</object>
 			<object class="NSMutableDictionary" key="flattenedProperties">
@@ -2043,6 +2247,7 @@
 					<bool key="EncodedWithXMLCoder">YES</bool>
 					<string>129.IBPluginDependency</string>
 					<string>129.ImportedFromIB2</string>
+					<string>130.IBEditorWindowLastContentRect</string>
 					<string>130.IBPluginDependency</string>
 					<string>130.ImportedFromIB2</string>
 					<string>131.IBPluginDependency</string>
@@ -2131,6 +2336,7 @@
 					<string>238.ImportedFromIB2</string>
 					<string>239.IBPluginDependency</string>
 					<string>239.ImportedFromIB2</string>
+					<string>24.IBEditorWindowLastContentRect</string>
 					<string>24.IBPluginDependency</string>
 					<string>24.ImportedFromIB2</string>
 					<string>240.IBPluginDependency</string>
@@ -2158,6 +2364,7 @@
 					<string>270.ImportedFromIB2</string>
 					<string>274.IBPluginDependency</string>
 					<string>274.ImportedFromIB2</string>
+					<string>275.IBEditorWindowLastContentRect</string>
 					<string>275.IBPluginDependency</string>
 					<string>275.ImportedFromIB2</string>
 					<string>281.IBPluginDependency</string>
@@ -2184,10 +2391,22 @@
 					<string>349.IBPluginDependency</string>
 					<string>350.IBPluginDependency</string>
 					<string>351.IBPluginDependency</string>
+					<string>369.IBPluginDependency</string>
+					<string>370.IBEditorWindowLastContentRect</string>
+					<string>370.IBPluginDependency</string>
+					<string>371.IBPluginDependency</string>
+					<string>373.IBPluginDependency</string>
+					<string>375.IBPluginDependency</string>
+					<string>377.IBPluginDependency</string>
+					<string>379.IBPluginDependency</string>
+					<string>380.IBPluginDependency</string>
+					<string>383.IBPluginDependency</string>
+					<string>384.IBPluginDependency</string>
 					<string>5.IBPluginDependency</string>
 					<string>5.ImportedFromIB2</string>
 					<string>56.IBPluginDependency</string>
 					<string>56.ImportedFromIB2</string>
+					<string>57.IBEditorWindowLastContentRect</string>
 					<string>57.IBPluginDependency</string>
 					<string>57.ImportedFromIB2</string>
 					<string>58.IBPluginDependency</string>
@@ -2199,6 +2418,7 @@
 					<bool key="EncodedWithXMLCoder">YES</bool>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<boolean value="YES"/>
+					<string>{{576, 728}, {64, 6}}</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<boolean value="YES"/>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -2231,9 +2451,9 @@
 						<bytes key="NSTransformStruct">P4AAAL+AAABBiAAAwyEAAA</bytes>
 					</object>
 					<boolean value="YES"/>
-					<string>{{337, 406}, {530, 190}}</string>
+					<string>{{368, 418}, {530, 190}}</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
-					<string>{{337, 406}, {530, 190}}</string>
+					<string>{{368, 418}, {530, 190}}</string>
 					<boolean value="YES"/>
 					<boolean value="YES"/>
 					<boolean value="YES"/>
@@ -2274,13 +2494,13 @@
 					<boolean value="YES"/>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<object class="NSAffineTransform">
-						<bytes key="NSTransformStruct">P4AAAL+AAABBYAAAwgQAAA</bytes>
+						<bytes key="NSTransformStruct">P4AAAL+AAABBYAAAwigAAA</bytes>
 					</object>
 					<boolean value="YES"/>
 					<boolean value="YES"/>
-					<string>{{329, 484}, {480, 316}}</string>
+					<string>{{421, 438}, {480, 316}}</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
-					<string>{{329, 484}, {480, 316}}</string>
+					<string>{{421, 438}, {480, 316}}</string>
 					<boolean value="YES"/>
 					<boolean value="YES"/>
 					<string>{213, 107}</string>
@@ -2303,6 +2523,7 @@
 					<boolean value="YES"/>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<boolean value="YES"/>
+					<string>{{469, 741}, {194, 73}}</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<boolean value="YES"/>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -2330,6 +2551,7 @@
 					<boolean value="YES"/>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<boolean value="YES"/>
+					<string>{{425, 661}, {151, 153}}</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<boolean value="YES"/>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -2340,7 +2562,7 @@
 					<boolean value="YES"/>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<boolean value="YES"/>
-					<string>{{329, 814}, {223, 20}}</string>
+					<string>{{329, 814}, {272, 20}}</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<boolean value="YES"/>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -2357,9 +2579,21 @@
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{540, 701}, {238, 113}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<boolean value="YES"/>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<boolean value="YES"/>
+					<string>{{341, 611}, {235, 203}}</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<boolean value="YES"/>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -2384,7 +2618,7 @@
 				</object>
 			</object>
 			<nil key="sourceID"/>
-			<int key="maxID">351</int>
+			<int key="maxID">385</int>
 		</object>
 		<object class="IBClassDescriber" key="IBDocument.Classes">
 			<object class="NSMutableArray" key="referencedPartialClassDescriptions">
@@ -2410,6 +2644,7 @@
 				</object>
 				<object class="IBPartialClassDescription">
 					<string key="className">FirstResponder</string>
+					<string key="superclassName">NSObject</string>
 					<object class="IBClassDescriptionSource" key="sourceIdentifier">
 						<string key="majorKey">IBUserSource</string>
 						<string key="minorKey"/>
@@ -2560,6 +2795,11 @@
 						<object class="NSArray" key="dict.sortedKeys">
 							<bool key="EncodedWithXMLCoder">YES</bool>
 							<string>launch:</string>
+							<string>openCMDLINE:</string>
+							<string>openCOPYING:</string>
+							<string>openDocumentation:</string>
+							<string>openINSTALL:</string>
+							<string>openREADME:</string>
 							<string>openTerminal:</string>
 							<string>runSetup:</string>
 						</object>
@@ -2568,6 +2808,11 @@
 							<string>id</string>
 							<string>id</string>
 							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
 						</object>
 					</object>
 					<object class="NSMutableDictionary" key="actionInfosByName">
@@ -2575,6 +2820,11 @@
 						<object class="NSArray" key="dict.sortedKeys">
 							<bool key="EncodedWithXMLCoder">YES</bool>
 							<string>launch:</string>
+							<string>openCMDLINE:</string>
+							<string>openCOPYING:</string>
+							<string>openDocumentation:</string>
+							<string>openINSTALL:</string>
+							<string>openREADME:</string>
 							<string>openTerminal:</string>
 							<string>runSetup:</string>
 						</object>
@@ -2582,6 +2832,26 @@
 							<bool key="EncodedWithXMLCoder">YES</bool>
 							<object class="IBActionInfo">
 								<string key="name">launch:</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBActionInfo">
+								<string key="name">openCMDLINE:</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBActionInfo">
+								<string key="name">openCOPYING:</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBActionInfo">
+								<string key="name">openDocumentation:</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBActionInfo">
+								<string key="name">openINSTALL:</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBActionInfo">
+								<string key="name">openREADME:</string>
 								<string key="candidateClassName">id</string>
 							</object>
 							<object class="IBActionInfo">
binary files a/pkg/osx/Resources/launcher.nib/keyedobjects.nib b/pkg/osx/Resources/launcher.nib/keyedobjects.nib differ
binary files /dev/null b/pkg/osx/disk/background.png differ
binary files /dev/null b/pkg/osx/disk/dir.DS_Store differ
--- /dev/null
+++ b/pkg/osx/dmgfix
@@ -1,0 +1,64 @@
+#!/usr/bin/osascript
+--
+-- Copyright(C) 2009 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.
+--
+--
+-- AppleScript script to automatically set the view properties in a
+-- .dmg file - ie. the background image, other Finder view options
+-- and icon positions.
+--
+-- Usage: dmgfix <absolute path to dmg> <disk name> <name of app>
+--
+
+on run argv
+    set dmgFile to POSIX file (item 1 of argv)
+    set diskName to item 2 of argv
+    set appName to item 3 of argv
+
+    tell application "Finder"
+        --activate
+        open dmgFile
+        delay 1
+        set win to the front Finder window
+        set theDisk to disk diskName
+        set the target of win to theDisk
+
+        -- window options:
+
+        set bgfile to file "background.png" of theDisk
+        set the bounds of win to {200, 200, 717, 536}
+        set icon size of icon view options of win to 96
+        set background picture of icon view options of win to bgfile
+        set toolbar visible of win to false
+
+        -- hide background file:
+
+        set bgloc to quoted form of POSIX path of (bgfile as text)
+        do shell script "SetFile -a V " & bgloc
+
+        -- icon positions:
+
+        set position of file "README" of theDisk to {120, 250}
+        set position of file "Software License" of theDisk to {380, 250}
+        set position of file appName of theDisk to {70, 110}
+        set position of file "Applications" of theDisk to {450, 110}
+
+        eject theDisk
+    end tell
+end run
+
--- a/pkg/win32/GNUmakefile
+++ b/pkg/win32/GNUmakefile
@@ -29,6 +29,10 @@
 		cp $(TOPLEVEL)/$$f staging/$$f.txt;  \
 		unix2dos staging/$$f.txt;            \
 	done
+	$(TOPLEVEL)/man/simplecpp -D_WIN32 -DPRECOMPILED  \
+	         < $(TOPLEVEL)/man/INSTALL.template       \
+	         > staging/INSTALL.txt
+	unix2dos staging/INSTALL.txt
 
 clean:
 	rm -f $(ZIP)
--- a/rpm.spec.in
+++ b/rpm.spec.in
@@ -49,14 +49,8 @@
 %files
 %doc %{_mandir}/man5/*
 %doc %{_mandir}/man6/*
-%doc README
-%doc README.OPL
-%doc INSTALL
-%doc NEWS
-%doc AUTHORS
-%doc COPYING
-%doc CMDLINE
-%doc BUGS
-%doc NOT-BUGS
+/usr/share/doc/@PACKAGE@/*
 /usr/games/*
+/usr/share/icons/*
+/usr/share/applications/*
 
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -191,6 +191,18 @@
         icon.c                      \
         doom-screensaver.desktop.in
 
+appdir = $(prefix)/share/applications
+app_DATA = @[email protected]
+
+@[email protected] : doom.desktop
+	cp $^ $@
+
+screensaverdir = $(prefix)/share/applications/screensavers
+screensaver_DATA = @[email protected]
+
+@[email protected]: doom-screensaver.desktop
+	cp $^ $@
+
 .rc.o:
 	$(WINDRES) $^ -o $@
 %.o : %.rc
--- a/src/d_mode.h
+++ b/src/d_mode.h
@@ -65,6 +65,7 @@
     exe_hacx,        // Hacx
     exe_ultimate,    // Ultimate Doom (retail)
     exe_final,       // Final Doom
+    exe_final2,      // Final Doom (alternate exe)
     exe_chex,        // Chex Quest executable (based on Final Doom)
 
     exe_heretic_1_3, // Heretic 1.3
--- a/src/deh_io.c
+++ b/src/deh_io.c
@@ -181,7 +181,7 @@
 
 int DEH_GetChar(deh_context_t *context)
 {
-    int result;
+    int result = 0;
 
     // Read characters, but ignore carriage returns
     // Essentially this is a DOS->Unix conversion
--- a/src/doom-screensaver.desktop.in
+++ b/src/doom-screensaver.desktop.in
@@ -1,10 +1,10 @@
 
 [Desktop Entry]
 Encoding=UTF-8
-Name=Doom
-Comment=DOOM by Id Software.
-TryExec=@PACKAGE_TARNAME@
-Exec=@PACKAGE_TARNAME@
+Name=@PACKAGE_NAME@
+Comment=@PACKAGE_SHORTDESC@
+TryExec=@PROGRAM_PREFIX@doom
+Exec=@PROGRAM_PREFIX@doom
 StartupNotify=false
 Terminal=false
 Type=Application
--- /dev/null
+++ b/src/doom.desktop.in
@@ -1,0 +1,7 @@
+[Desktop Entry]
+Name=@PACKAGE_NAME@
+Exec=@PROGRAM_PREFIX@doom
+Icon=@PROGRAM_PREFIX@doom
+Type=Application
+Comment=@PACKAGE_SHORTDESC@
+Categories=Game;ActionGame;
--- a/src/doom/d_main.c
+++ b/src/doom/d_main.c
@@ -532,6 +532,9 @@
     // include a DEMO4 lump, so the game bombs out with an error
     // when it reaches this point in the demo sequence.
 
+    // However! There is an alternate version of Final Doom that
+    // includes a fixed executable.
+
     if (gameversion == exe_ultimate || gameversion == exe_final)
       demosequence = (demosequence+1)%7;
     else
@@ -914,6 +917,7 @@
     {"Hacx",                 "hacx",       exe_hacx},
     {"Ultimate Doom",        "ultimate",   exe_ultimate},
     {"Final Doom",           "final",      exe_final},
+    {"Final Doom (alt)",     "final2",     exe_final2},
     {"Chex Quest",           "chex",       exe_chex},
     { NULL,                  NULL,         0},
 };
@@ -930,7 +934,7 @@
     // @category compat
     //
     // Emulate a specific version of Doom.  Valid values are "1.9",
-    // "ultimate" and "final".
+    // "ultimate", "final", "final2", "hacx" and "chex".
     //
 
     p = M_CheckParmWithArgs("-gameversion", 1);
@@ -994,8 +998,10 @@
             else
             {
                 // Final Doom: tnt or plutonia
+                // Default to the "alt" version of the executable that
+                // fixes the demo loop behavior.
 
-                gameversion = exe_final;
+                gameversion = exe_final2;
             }
         }
     }
@@ -1034,11 +1040,37 @@
 
 static void LoadChexDeh(void)
 {
-    char *chex_deh;
+    char *chex_deh = NULL;
+    char *sep;
 
     if (gameversion == exe_chex)
     {
-        chex_deh = D_FindWADByName("chex.deh");
+        // Look for chex.deh in the same directory as the IWAD file.
+
+        sep = strrchr(iwadfile, DIR_SEPARATOR);
+
+        if (sep != NULL)
+        {
+            chex_deh = malloc(strlen(iwadfile) + 9);
+            strcpy(chex_deh, iwadfile);
+            chex_deh[sep - iwadfile + 1] = '\0';
+            strcat(chex_deh, "chex.deh");
+        }
+        else
+        {
+            chex_deh = strdup("chex.deh");
+        }
+
+        // If the dehacked patch isn't found, try searching the WAD
+        // search path instead.  We might find it...
+
+        if (!M_FileExists(chex_deh))
+        {
+            free(chex_deh);
+            chex_deh = D_FindWADByName("chex.deh");
+        }
+
+        // Still not found?
 
         if (chex_deh == NULL)
         {
--- a/src/doom/d_net.c
+++ b/src/doom/d_net.c
@@ -474,7 +474,8 @@
     // Start a multiplayer server, listening for connections.
     //
 
-    if (M_CheckParm("-server") > 0)
+    if (M_CheckParm("-server") > 0
+     || M_CheckParm("-privateserver") > 0)
     {
         NET_SV_Init();
         NET_SV_AddModule(&net_loop_server_module);
--- /dev/null
+++ b/src/doom/doom.desktop.in
@@ -1,0 +1,7 @@
+[Desktop Entry]
+Name=@PACKAGE_NAME@
+Exec=@PROGRAM_PREFIX@doom
+Icon=@PROGRAM_PREFIX@doom
+Type=Application
+Comment=@PACKAGE_SHORTDESC@
+Categories=Game;ActionGame;
--- a/src/doom/doomstat.c
+++ b/src/doom/doomstat.c
@@ -32,7 +32,7 @@
 // Game Mode - identify IWAD as shareware, retail etc.
 GameMode_t gamemode = indetermined;
 GameMission_t	gamemission = doom;
-GameVersion_t   gameversion = exe_final;
+GameVersion_t   gameversion = exe_final2;
 char *gamedescription;
 
 // Set if homebrew PWAD stuff has been added.
--- a/src/doom/g_game.c
+++ b/src/doom/g_game.c
@@ -361,6 +361,13 @@
 
 static boolean WeaponSelectable(weapontype_t weapon)
 {
+    // Can't select the super shotgun in Doom 1.
+
+    if (weapon == wp_supershotgun && gamemission == doom)
+    {
+        return false;
+    }
+
     // Can't select a weapon if we don't own it.
 
     if (!players[consoleplayer].weaponowned[weapon])
--- a/src/doom/m_menu.c
+++ b/src/doom/m_menu.c
@@ -798,6 +798,7 @@
             break;
 
         case exe_final:
+        case exe_final2:
 
             // Final Doom always displays "HELP".
 
--- a/src/doom/p_map.c
+++ b/src/doom/p_map.c
@@ -885,17 +885,8 @@
 	
 	dist = FixedMul (attackrange, in->frac);
 
-        // Return false if there is no back sector.  This should never
-        // be the case if the line is two-sided; however, some WADs
-        // (eg. ottawau.wad) use this as an "impassible glass" trick
-        // and rely on Vanilla Doom's (unintentional) support for this.
-
-        if (li->backsector == NULL)
-        {
-            return false;
-        }
-
-        if (li->frontsector->floorheight != li->backsector->floorheight)
+        if (li->backsector == NULL
+         || li->frontsector->floorheight != li->backsector->floorheight)
 	{
 	    slope = FixedDiv (openbottom - shootz , dist);
 	    if (slope > bottomslope)
@@ -902,7 +893,8 @@
 		bottomslope = slope;
 	}
 		
-	if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
+	if (li->backsector == NULL
+         || li->frontsector->ceilingheight != li->backsector->ceilingheight)
 	{
 	    slope = FixedDiv (opentop - shootz , dist);
 	    if (slope < topslope)
@@ -983,26 +975,35 @@
 		
 	dist = FixedMul (attackrange, in->frac);
 
-        // Check if backsector is NULL.  See comment in PTR_AimTraverse.
+        // e6y: emulation of missed back side on two-sided lines.
+        // backsector can be NULL when emulating missing back side.
 
-	if (li->backsector == NULL)
+        if (li->backsector == NULL)
         {
-            goto hitline;
+            slope = FixedDiv (openbottom - shootz , dist);
+            if (slope > aimslope)
+                goto hitline;
+
+            slope = FixedDiv (opentop - shootz , dist);
+            if (slope < aimslope)
+                goto hitline;
         }
+        else
+        {
+            if (li->frontsector->floorheight != li->backsector->floorheight)
+            {
+                slope = FixedDiv (openbottom - shootz , dist);
+                if (slope > aimslope)
+                    goto hitline;
+            }
 
-        if (li->frontsector->floorheight != li->backsector->floorheight)
-	{
-	    slope = FixedDiv (openbottom - shootz , dist);
-	    if (slope > aimslope)
-		goto hitline;
-	}
-		
-	if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
-	{
-	    slope = FixedDiv (opentop - shootz , dist);
-	    if (slope < aimslope)
-		goto hitline;
-	}
+            if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
+            {
+                slope = FixedDiv (opentop - shootz , dist);
+                if (slope < aimslope)
+                    goto hitline;
+            }
+        }
 
 	// shot continues
 	return true;
--- a/src/doom/p_setup.c
+++ b/src/doom/p_setup.c
@@ -155,8 +155,25 @@
     W_ReleaseLumpNum(lump);
 }
 
+//
+// GetSectorAtNullAddress
+//
+sector_t* GetSectorAtNullAddress(void)
+{
+    static boolean null_sector_is_initialized = false;
+    static sector_t null_sector;
 
+    if (!null_sector_is_initialized)
+    {
+        memset(&null_sector, 0, sizeof(null_sector));
+        I_GetMemoryValue(0, &null_sector.floorheight, 4);
+        I_GetMemoryValue(4, &null_sector.ceilingheight, 4);
+        null_sector_is_initialized = true;
+    }
 
+    return &null_sector;
+}
+
 //
 // P_LoadSegs
 //
@@ -204,10 +221,12 @@
 
             if (sidenum < 0 || sidenum >= numsides)
             {
-                sidenum = 0;
+                li->backsector = GetSectorAtNullAddress();
             }
-
-            li->backsector = sides[sidenum].sector;
+            else
+            {
+                li->backsector = sides[sidenum].sector;
+            }
         }
         else
         {
@@ -681,7 +700,7 @@
     if (len > sizeof(rejectpad))
     {
         fprintf(stderr, "PadRejectArray: REJECT lump too short to pad! (%i > %i)\n",
-                        len, sizeof(rejectpad));
+                        len, (int) sizeof(rejectpad));
 
         // Pad remaining space with 0 (or 0xff, if specified on command line).
 
--- a/src/doom/st_stuff.c
+++ b/src/doom/st_stuff.c
@@ -525,8 +525,13 @@
 	
 	plyr->message = DEH_String(STSTR_MUS);
 	cht_GetParam(&cheat_mus, buf);
-	
-	if (gamemode == commercial)
+
+        // Note: The original v1.9 had a bug that tried to play back
+        // the Doom II music regardless of gamemode.  This was fixed
+        // in the Ultimate Doom executable so that it would work for
+        // the Doom 1 music as well.
+
+	if (gamemode == commercial || gameversion < exe_ultimate)
 	{
 	  musnum = mus_runnin + (buf[0]-'0')*10 + buf[1]-'0' - 1;
 	  
--- a/src/i_endoom.c
+++ b/src/i_endoom.c
@@ -67,7 +67,7 @@
     {
         TXT_UpdateScreen();
 
-        if (TXT_GetChar() >= 0)
+        if (TXT_GetChar() > 0)
         {
             break;
         }
--- a/src/i_oplmusic.c
+++ b/src/i_oplmusic.c
@@ -1079,7 +1079,7 @@
 
 // Restart a song from the beginning.
 
-static void RestartSong(void)
+static void RestartSong(void *unused)
 {
     unsigned int i;
 
@@ -1117,10 +1117,15 @@
         --running_tracks;
 
         // When all tracks have finished, restart the song.
+        // Don't restart the song immediately, but wait for 5ms
+        // before triggering a restart.  Otherwise it is possible
+        // to construct an empty MIDI file that causes the game
+        // to lock up in an infinite loop. (5ms should be short
+        // enough not to be noticeable by the listener).
 
         if (running_tracks <= 0 && song_looping)
         {
-            RestartSong();
+            OPL_SetCallback(5, RestartSong, NULL);
         }
 
         return;
--- a/src/i_scale.c
+++ b/src/i_scale.c
@@ -33,6 +33,7 @@
 #include "doomtype.h"
 
 #include "i_video.h"
+#include "m_argv.h"
 #include "z_zone.h"
 
 #if defined(_MSC_VER) && !defined(__cplusplus)
@@ -965,6 +966,21 @@
         // 100% line 0
         WriteLine5x(screenp, bufp);
         screenp += dest_pitch; bufp += SCREENWIDTH;
+    }
+
+    // test hack for Porsche Monty... scan line simulation:
+    // See here: http://www.doomworld.com/vb/post/962612
+
+    if (M_CheckParm("-scanline") > 0)
+    {
+        screenp = (byte *) dest_buffer + 2 * dest_pitch;
+
+        for (y=0; y<1198; y += 3)
+        {
+            memset(screenp, 0, 1600);
+
+            screenp += dest_pitch * 3;
+        }
     }
 
     return true;
--- a/src/i_system.c
+++ b/src/i_system.c
@@ -55,6 +55,10 @@
 #include "w_wad.h"
 #include "z_zone.h"
 
+#ifdef __MACOSX__
+#include <CoreFoundation/CFUserNotification.h>
+#endif
+
 #define DEFAULT_RAM 16 /* MiB */
 #define MIN_RAM     4  /* MiB */
 
@@ -282,7 +286,6 @@
 }
 */
 
-
 //
 // I_Quit
 //
@@ -366,8 +369,151 @@
     }
 #endif
 
+#ifdef __MACOSX__
+    {
+        CFStringRef message;
+        char msgbuf[512];
+	int i;
+
+        va_start(argptr, error);
+        memset(msgbuf, 0, sizeof(msgbuf));
+        vsnprintf(msgbuf, sizeof(msgbuf) - 1, error, argptr);
+        va_end(argptr);
+
+	// The CoreFoundation message box wraps text lines, so replace
+	// newline characters with spaces so that multiline messages
+	// are continuous.
+
+	for (i = 0; msgbuf[i] != '\0'; ++i)
+        {
+            if (msgbuf[i] == '\n')
+            {
+                msgbuf[i] = ' ';
+            }
+        }
+
+        message = CFStringCreateWithCString(NULL, msgbuf,
+                                            kCFStringEncodingUTF8);
+
+        CFUserNotificationDisplayNotice(0,
+                                        kCFUserNotificationCautionAlertLevel,
+                                        NULL,
+                                        NULL,
+                                        NULL,
+                                        CFSTR(PACKAGE_STRING),
+                                        message,
+                                        NULL);
+    }
+#endif
+
     // abort();
 
     exit(-1);
+}
+
+//
+// Read Access Violation emulation.
+//
+// From PrBoom+, by entryway.
+//
+
+// C:\>debug
+// -d 0:0
+//
+// DOS 6.22:
+// 0000:0000  (57 92 19 00) F4 06 70 00-(16 00)
+// DOS 7.1:
+// 0000:0000  (9E 0F C9 00) 65 04 70 00-(16 00)
+// Win98:
+// 0000:0000  (9E 0F C9 00) 65 04 70 00-(16 00)
+// DOSBox under XP:
+// 0000:0000  (00 00 00 F1) ?? ?? ?? 00-(07 00)
+
+#define DOS_MEM_DUMP_SIZE 10
+
+static const unsigned char mem_dump_dos622[DOS_MEM_DUMP_SIZE] = {
+  0x57, 0x92, 0x19, 0x00, 0xF4, 0x06, 0x70, 0x00, 0x16, 0x00};
+static const unsigned char mem_dump_win98[DOS_MEM_DUMP_SIZE] = {
+  0x9E, 0x0F, 0xC9, 0x00, 0x65, 0x04, 0x70, 0x00, 0x16, 0x00};
+static const unsigned char mem_dump_dosbox[DOS_MEM_DUMP_SIZE] = {
+  0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00};
+static unsigned char mem_dump_custom[DOS_MEM_DUMP_SIZE];
+
+static const unsigned char *dos_mem_dump = mem_dump_dos622;
+
+boolean I_GetMemoryValue(unsigned int offset, void *value, int size)
+{
+    static boolean firsttime = true;
+
+    if (firsttime)
+    {
+        int p, i, val;
+
+        firsttime = false;
+        i = 0;
+
+        //!
+        // @category compat
+        // @arg <version>
+        //
+        // Specify DOS version to emulate for NULL pointer dereference
+        // emulation.  Supported versions are: dos622, dos71, dosbox.
+        // The default is to emulate DOS 7.1 (Windows 98).
+        //
+
+        p = M_CheckParmWithArgs("-setmem", 1);
+
+        if (p > 0)
+        {
+            if (!strcasecmp(myargv[p + 1], "dos622"))
+            {
+                dos_mem_dump = mem_dump_dos622;
+            }
+            if (!strcasecmp(myargv[p + 1], "dos71"))
+            {
+                dos_mem_dump = mem_dump_win98;
+            }
+            else if (!strcasecmp(myargv[p + 1], "dosbox"))
+            {
+                dos_mem_dump = mem_dump_dosbox;
+            }
+            else
+            {
+                for (i = 0; i < DOS_MEM_DUMP_SIZE; ++i)
+                {
+                    ++p;
+
+                    if (p >= myargc || myargv[p][0] == '-')
+                    {
+                        break;
+                    }
+
+                    M_StrToInt(myargv[p], &val);
+                    mem_dump_custom[i++] = (unsigned char) val;
+                }
+
+                dos_mem_dump = mem_dump_custom;
+            }
+        }
+    }
+
+    switch (size)
+    {
+    case 1:
+        *((unsigned char *) value) = dos_mem_dump[offset];
+        return true;
+    case 2:
+        *((unsigned short *) value) = dos_mem_dump[offset]
+                                    | (dos_mem_dump[offset + 1] << 8);
+        return true;
+    case 4:
+        *((unsigned int *) value) = dos_mem_dump[offset]
+                                  | (dos_mem_dump[offset + 1] << 8)
+                                  | (dos_mem_dump[offset + 2] << 16)
+                                  | (dos_mem_dump[offset + 3] << 24);
+        return true;
+    }
+
+    return false;
 }
 
--- a/src/i_system.h
+++ b/src/i_system.h
@@ -71,6 +71,8 @@
 
 void I_Error (char *error, ...);
 
+boolean I_GetMemoryValue(unsigned int offset, void *value, int size);
+
 // Schedule a function to be called when the program exits.
 // If run_if_error is true, the function is called if the exit
 // is due to an error (I_Error)
--- a/src/i_video.c
+++ b/src/i_video.c
@@ -656,7 +656,7 @@
     {
         // Unicode value, from key layout.
 
-        return event->key.keysym.unicode;
+        return tolower(event->key.keysym.unicode);
     }
 }
 
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -209,8 +209,9 @@
 
 void M_ExtractFileBase(char *path, char *dest)
 {
-    char*	src;
-    int		length;
+    char *src;
+    char *filename;
+    int length;
 
     src = path + strlen(path) - 1;
 
@@ -220,20 +221,26 @@
 	src--;
     }
 
-    // copy up to eight characters
-    memset(dest, 0, 8);
+    filename = src;
+
+    // Copy up to eight characters
+    // Note: Vanilla Doom exits with an error if a filename is specified
+    // with a base of more than eight characters.  To remove the 8.3
+    // filename limit, instead we simply truncate the name.
+
     length = 0;
+    memset(dest, 0, 8);
 
     while (*src != '\0' && *src != '.')
     {
-        ++length;
-
-	if (length > 8)
+        if (length >= 8)
         {
-	    I_Error ("Filename base of %s >8 chars", path);
+            printf("Warning: Truncated '%s' lump name to '%.8s'.\n",
+                   filename, dest);
+            break;
         }
 
-	*dest++ = toupper((int)*src++);
+	dest[length++] = toupper((int)*src++);
     }
 }
 
--- a/src/net_server.c
+++ b/src/net_server.c
@@ -1563,6 +1563,7 @@
 {
     //!
     // When running a server, don't register with the global master server.
+    // Implies -server.
     //
     // @category net
     //
--- a/src/setup/Makefile.am
+++ b/src/setup/Makefile.am
@@ -27,6 +27,12 @@
 EXTRA_DIST=                                     \
     setup_icon.c
 
+appdir = $(prefix)/share/applications
+app_DATA = @[email protected]
+
+@[email protected] : setup.desktop
+	cp $^ $@
+
 if HAVE_PYTHON
 
 setup_icon.c : $(top_builddir)/data/setup8.ico
--- a/src/setup/display.c
+++ b/src/setup/display.c
@@ -527,25 +527,34 @@
     GenerateModesTable(NULL, modes_table);
 }
 
-#if defined(_WIN32) && !defined(_WIN32_WCE)
+static void UpdateModeSeparator(TXT_UNCAST_ARG(widget),
+                                TXT_UNCAST_ARG(separator))
+{
+    TXT_CAST_ARG(txt_separator_t, separator);
 
-static int win32_video_driver = 0;
+    if (fullscreen)
+    {
+        TXT_SetSeparatorLabel(separator, "Screen mode");
+    }
+    else
+    {
+        TXT_SetSeparatorLabel(separator, "Window size");
+    }
+}
 
-static char *win32_video_drivers[] = 
-{
-    "DirectX",
-    "Windows GDI",
-};
+#if defined(_WIN32) && !defined(_WIN32_WCE)
 
+static int use_directx = 1;
+
 static void SetWin32VideoDriver(void)
 {
     if (!strcmp(video_driver, "windib"))
     {
-        win32_video_driver = 1;
+        use_directx = 0;
     }
     else
     {
-        win32_video_driver = 0;
+        use_directx = 1;
     }
 }
 
@@ -553,14 +562,16 @@
                               TXT_UNCAST_ARG(modes_table))
 {
     TXT_CAST_ARG(txt_table_t, modes_table);
-    char *drivers[] = 
+
+    if (use_directx)
     {
-        "",
-        "windib",
-    };
+        video_driver = "";
+    }
+    else
+    {
+        video_driver = "windib";
+    }
 
-    video_driver = drivers[win32_video_driver != 0];
-
     // When the video driver is changed, we need to restart the textscreen 
     // library.
 
@@ -579,16 +590,72 @@
 
 #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;
-    txt_checkbox_t *ar_checkbox;
-    txt_dropdown_list_t *bpp_selector;
+    int i;
     int num_columns;
+    int num_rows;
     int window_y;
 
     // What color depths are supported?  Generate supported_bpps array
@@ -606,15 +673,9 @@
     }
 
     // Open the window
-    
+
     window = TXT_NewWindow("Display Configuration");
 
-    TXT_AddWidgets(window, 
-                   fs_checkbox = TXT_NewCheckBox("Fullscreen", &fullscreen),
-                   ar_checkbox = TXT_NewCheckBox("Correct aspect ratio",
-                                                 &aspect_ratio_correct),
-                   NULL);
-
     // Some machines can have lots of video modes.  This tries to
     // keep a limit of six lines by increasing the number of
     // columns.  In extreme cases, the window is moved up slightly.
@@ -621,13 +682,11 @@
 
     BuildFullscreenModesList();
 
-    window_y = 5;
-
-    if (num_screen_modes_fullscreen <= 18)
+    if (num_screen_modes_fullscreen <= 24)
     {
         num_columns = 3;
     }
-    else if (num_screen_modes_fullscreen <= 24)
+    else if (num_screen_modes_fullscreen <= 40)
     {
         num_columns = 4;
     }
@@ -634,80 +693,90 @@
     else
     {
         num_columns = 5;
-        window_y -= 3;
     }
 
     modes_table = TXT_NewTable(num_columns);
 
-    // The window is set at a fixed vertical position.  This keeps
-    // the top of the window stationary when switching between
-    // fullscreen and windowed mode (which causes the window's
-    // height to change).
+    // Build window:
 
-    TXT_SetWindowPosition(window, TXT_HORIZ_CENTER, TXT_VERT_TOP, 
-                                  TXT_SCREEN_W / 2, window_y);
+    TXT_AddWidget(window, 
+                  fs_checkbox = TXT_NewCheckBox("Full screen", &fullscreen));
 
-    // On Windows, there is an extra control to change between 
-    // the Windows GDI and DirectX video drivers.
-
-#if defined(_WIN32) && !defined(_WIN32_WCE)
+    if (num_supported_bpps > 1)
     {
-        txt_table_t *driver_table;
-        txt_dropdown_list_t *driver_list;
-
-        driver_table = TXT_NewTable(2);
-
-        TXT_SetColumnWidths(driver_table, 20, 0);
-
-        TXT_AddWidgets(driver_table,
-                       TXT_NewLabel("Video driver"),
-                       driver_list = TXT_NewDropdownList(&win32_video_driver,
-                                                         win32_video_drivers,
-                                                         2),
+        TXT_AddWidgets(window,
+                       TXT_NewSeparator("Color depth"),
+                       bpp_table = TXT_NewTable(4),
                        NULL);
 
-        TXT_SignalConnect(driver_list, "changed",
-                          UpdateVideoDriver, modes_table);
-        SetWin32VideoDriver();
+        for (i = 0; i < num_supported_bpps; ++i)
+        {
+            txt_radiobutton_t *button;
 
-        TXT_AddWidget(window, driver_table);
+            button = TXT_NewRadioButton(supported_bpps[i],
+                                        &selected_bpp, i);
+
+            TXT_AddWidget(bpp_table, button);
+            TXT_SignalConnect(button, "selected", UpdateBPP, modes_table);
+        }
     }
-#endif
 
-    // Screen modes list
-
     TXT_AddWidgets(window,
-                   TXT_NewSeparator("Screen mode"),
-                   bpp_table = TXT_NewTable(2),
+                   modes_separator = TXT_NewSeparator(""),
                    modes_table,
-                   TXT_NewSeparator("Misc."),
                    NULL);
 
-    if (gamemission == heretic || gamemission == hexen)
+    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)
     {
-        TXT_AddWidget(window,
-                      TXT_NewCheckBox("Graphical startup", &graphical_startup));
+        num_rows = 4;
     }
 
-    if (gamemission == doom || gamemission == heretic)
+    if (num_supported_bpps > 1)
     {
-        TXT_AddWidget(window,
-                      TXT_NewCheckBox("Show ENDOOM screen", &show_endoom));
+        num_rows += 2;
     }
 
-    TXT_AddWidgets(bpp_table,
-                   TXT_NewLabel("Color depth: "),
-                   bpp_selector = TXT_NewDropdownList(&selected_bpp,
-                                                      supported_bpps,
-                                                      num_supported_bpps),
-                   NULL);
+    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_SignalConnect(bpp_selector, "changed", UpdateBPP, modes_table);
-    TXT_SignalConnect(fs_checkbox, "changed", GenerateModesTable, modes_table);
-    TXT_SignalConnect(ar_checkbox, "changed", GenerateModesTable, modes_table);
+    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)
--- a/src/setup/joystick.c
+++ b/src/setup/joystick.c
@@ -27,6 +27,7 @@
 #include "m_controls.h"
 #include "textscreen.h"
 
+#include "execute.h"
 #include "joystick.h"
 #include "mode.h"
 #include "txt_joybinput.h"
@@ -435,6 +436,8 @@
 
     TXT_SignalConnect(joystick_button, "pressed", CalibrateJoystick, NULL);
     TXT_SignalConnect(window, "closed", JoystickWindowClosed, NULL);
+
+    TXT_SetWindowAction(window, TXT_HORIZ_CENTER, TestConfigAction());
 
     SetJoystickButtonLabel();
 }
--- a/src/setup/setup-manifest.xml.in
+++ b/src/setup/setup-manifest.xml.in
@@ -1,16 +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="@[email protected]" processorArchitecture="*"
-                    name="@PROGRAM_PREFIX@setup" type="win32"/>
+  <assemblyIdentity version="0.0.0.0" processorArchitecture="X86"
+                    name="@[email protected]" type="win32"/>
   <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
     <security>
       <requestedPrivileges>
-        <!-- Hi Vista! We don't require elevated privileges. Thanks! -->
-        <requestedExecutionLevel level="asInvoker"/>
+        <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>
 
--- /dev/null
+++ b/src/setup/setup.desktop.in
@@ -1,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;
--- a/src/setup/sound.c
+++ b/src/setup/sound.c
@@ -68,7 +68,7 @@
 
 int snd_sfxdevice = SNDDEVICE_SB;
 int snd_musicdevice = SNDDEVICE_GENMIDI;
-int snd_samplerate = 22050;
+int snd_samplerate = 44100;
 int opl_io_port = 0x388;
 
 static int numChannels = 8;
--- a/src/setup/txt_joybinput.c
+++ b/src/setup/txt_joybinput.c
@@ -48,7 +48,12 @@
     if (event->type == SDL_JOYBUTTONDOWN)
     {
         *joystick_input->variable = event->jbutton.button;
-        TXT_EmitSignal(joystick_input, "set");
+
+        if (joystick_input->check_conflicts)
+        {
+            TXT_EmitSignal(joystick_input, "set");
+        }
+
         TXT_CloseWindow(joystick_input->prompt_window);
         return 1;
     }
@@ -89,6 +94,10 @@
     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;
@@ -153,15 +162,7 @@
         GetJoystickButtonDescription(*joystick_input->variable, buf);
     }
 
-    if (selected)
-    {
-        TXT_BGColor(TXT_COLOR_GREY, 0);
-    }
-    else
-    {
-        TXT_BGColor(TXT_COLOR_BLUE, 0);
-    }
-
+    TXT_SetWidgetBG(joystick_input, selected);
     TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
     
     TXT_DrawString(buf);
--- a/src/setup/txt_joybinput.h
+++ b/src/setup/txt_joybinput.h
@@ -37,6 +37,7 @@
     txt_widget_t widget;
     int *variable;
     txt_window_t *prompt_window;
+    int check_conflicts;
 };
 
 txt_joystick_input_t *TXT_NewJoystickInput(int *variable);
--- a/src/setup/txt_keyinput.c
+++ b/src/setup/txt_keyinput.c
@@ -42,7 +42,12 @@
         // Got the key press.  Save to the variable and close the window.
 
         *key_input->variable = key;
-        TXT_EmitSignal(key_input, "set");
+
+        if (key_input->check_conflicts)
+        {
+            TXT_EmitSignal(key_input, "set");
+        }
+
         TXT_CloseWindow(window);
 
         // Re-enable key mappings now that we have the key
@@ -67,6 +72,10 @@
     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, 
@@ -118,15 +127,7 @@
         TXT_GetKeyDescription(*key_input->variable, buf);
     }
 
-    if (selected)
-    {
-        TXT_BGColor(TXT_COLOR_GREY, 0);
-    }
-    else
-    {
-        TXT_BGColor(TXT_COLOR_BLUE, 0);
-    }
-
+    TXT_SetWidgetBG(key_input, selected);
     TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
     
     TXT_DrawString(buf);
--- a/src/setup/txt_keyinput.h
+++ b/src/setup/txt_keyinput.h
@@ -35,6 +35,7 @@
 {
     txt_widget_t widget;
     int *variable;
+    int check_conflicts;
 };
 
 txt_key_input_t *TXT_NewKeyInput(int *variable);
--- a/src/setup/txt_mouseinput.c
+++ b/src/setup/txt_mouseinput.c
@@ -42,7 +42,12 @@
     // Got the mouse press.  Save to the variable and close the window.
 
     *mouse_input->variable = b - TXT_MOUSE_BASE;
-    TXT_EmitSignal(mouse_input, "set");
+
+    if (mouse_input->check_conflicts)
+    {
+        TXT_EmitSignal(mouse_input, "set");
+    }
+
     TXT_CloseWindow(window);
 
     return 1;
@@ -53,6 +58,9 @@
     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, 
@@ -111,15 +119,7 @@
         GetMouseButtonDescription(*mouse_input->variable, buf);
     }
 
-    if (selected)
-    {
-        TXT_BGColor(TXT_COLOR_GREY, 0);
-    }
-    else
-    {
-        TXT_BGColor(TXT_COLOR_BLUE, 0);
-    }
-
+    TXT_SetWidgetBG(mouse_input, selected);
     TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
     
     TXT_DrawString(buf);
--- a/src/setup/txt_mouseinput.h
+++ b/src/setup/txt_mouseinput.h
@@ -35,6 +35,7 @@
 {
     txt_widget_t widget;
     int *variable;
+    int check_conflicts;
 };
 
 txt_mouse_input_t *TXT_NewMouseInput(int *variable);
--- a/textscreen/txt_button.c
+++ b/textscreen/txt_button.c
@@ -46,16 +46,12 @@
 
     w = button->widget.w;
 
-    TXT_BGColor(TXT_COLOR_BLUE, 0);
     TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
 
-    if (selected)
-    {
-        TXT_BGColor(TXT_COLOR_GREY, 0);
-    }
+    TXT_SetWidgetBG(button, selected);
 
     TXT_DrawString(button->label);
-    
+
     for (i=strlen(button->label); i < w; ++i)
     {
         TXT_DrawString(" ");
--- a/textscreen/txt_checkbox.c
+++ b/textscreen/txt_checkbox.c
@@ -48,7 +48,7 @@
 
     w = checkbox->widget.w;
 
-    TXT_BGColor(TXT_COLOR_BLUE, 0);
+    TXT_BGColor(TXT_WINDOW_BACKGROUND, 0);
     TXT_FGColor(TXT_COLOR_BRIGHT_CYAN);
     TXT_DrawString("(");
 
@@ -67,11 +67,7 @@
 
     TXT_DrawString(") ");
 
-    if (selected)
-    {
-        TXT_BGColor(TXT_COLOR_GREY, 0);
-    }
-
+    TXT_SetWidgetBG(checkbox, selected);
     TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
 
     TXT_DrawString(checkbox->label);
--- a/textscreen/txt_desktop.c
+++ b/textscreen/txt_desktop.c
@@ -61,6 +61,16 @@
     num_windows = to;
 }
 
+txt_window_t *TXT_GetActiveWindow(void)
+{
+    if (num_windows == 0)
+    {
+        return NULL;
+    }
+
+    return all_windows[num_windows - 1];
+}
+
 static void DrawDesktopBackground(const char *title)
 {
     int i;
--- a/textscreen/txt_desktop.h
+++ b/textscreen/txt_desktop.h
@@ -63,6 +63,15 @@
 
 void TXT_GUIMainLoop(void);
 
+/**
+ * Get the top window on the desktop that is currently receiving
+ * inputs.
+ *
+ * @return    The active window, or NULL if no windows are present.
+ */
+
+txt_window_t *TXT_GetActiveWindow(void);
+
 #endif /* #ifndef TXT_DESKTOP_H */
 
 
--- a/textscreen/txt_dropdown.c
+++ b/textscreen/txt_dropdown.c
@@ -99,6 +99,22 @@
     return 0;
 }
 
+static int SelectorMouseListener(txt_window_t *window, int x, int y, int b,
+                                 void *unused)
+{
+    txt_widget_t *win;
+
+    win = (txt_widget_t *) window;
+
+    if (x < win->x || x > win->x + win->w || y < win->y || y > win->y + win->h)
+    {
+        TXT_CloseWindow(window);
+        return 1;
+    }
+
+    return 0;
+}
+
 // Open the dropdown list window to select an item
 
 static void OpenSelectorWindow(txt_dropdown_list_t *list)
@@ -158,6 +174,7 @@
     // Catch presses of escape in this window and close it.
 
     TXT_SetKeyListener(window, SelectorWindowListener, NULL);
+    TXT_SetMouseListener(window, SelectorMouseListener, NULL);
 }
 
 static int DropdownListWidth(txt_dropdown_list_t *list)
@@ -197,15 +214,7 @@
 
     // Set bg/fg text colors.
 
-    if (selected) 
-    {
-        TXT_BGColor(TXT_COLOR_GREY, 0);
-    }
-    else
-    {
-        TXT_BGColor(TXT_COLOR_BLUE, 0);
-    }
-
+    TXT_SetWidgetBG(list, selected);
     TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
 
     // Select a string to draw from the list, if the current value is
--- a/textscreen/txt_gui.c
+++ b/textscreen/txt_gui.c
@@ -131,7 +131,7 @@
     int bx, by;
 
     TXT_FGColor(TXT_COLOR_BRIGHT_CYAN);
-    TXT_BGColor(TXT_COLOR_BLUE, 0);
+    TXT_BGColor(TXT_WINDOW_BACKGROUND, 0);
 
     for (y1=y; y1<y+h; ++y1)
     {
@@ -191,7 +191,7 @@
     data = TXT_GetScreenData();
 
     TXT_FGColor(TXT_COLOR_BRIGHT_CYAN);
-    TXT_BGColor(TXT_COLOR_BLUE, 0);
+    TXT_BGColor(TXT_WINDOW_BACKGROUND, 0);
 
     if (!VALID_Y(y))
     {
--- a/textscreen/txt_gui.h
+++ b/textscreen/txt_gui.h
@@ -27,6 +27,9 @@
 #ifndef TXT_GUI_H
 #define TXT_GUI_H
 
+#define TXT_WINDOW_BACKGROUND TXT_COLOR_BLUE
+#define TXT_HOVER_BACKGROUND  TXT_COLOR_CYAN
+
 void TXT_DrawDesktopBackground(const char *title);
 void TXT_DrawWindowFrame(const char *title, int x, int y, int w, int h);
 void TXT_DrawSeparator(int x, int y, int w);
@@ -34,6 +37,7 @@
 
 void TXT_DrawHorizScrollbar(int x, int y, int w, int cursor, int range);
 void TXT_DrawVertScrollbar(int x, int y, int h, int cursor, int range);
+
 
 void TXT_InitClipArea(void);
 void TXT_PushClipArea(int x1, int x2, int y1, int y2);
--- a/textscreen/txt_inputbox.c
+++ b/textscreen/txt_inputbox.c
@@ -60,15 +60,9 @@
     {
         TXT_BGColor(TXT_COLOR_BLACK, 0);
     }
-    else if (selected)
-    {
-        TXT_BGColor(TXT_COLOR_GREY, 0);
-    }
     else
     {
-        // Not even selected
-
-        TXT_BGColor(TXT_COLOR_BLUE, 0);
+        TXT_SetWidgetBG(inputbox, selected);
     }
 
     TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
--- a/textscreen/txt_label.c
+++ b/textscreen/txt_label.c
@@ -176,7 +176,7 @@
 
     // Default colors
 
-    label->bgcolor = TXT_COLOR_BLUE;
+    label->bgcolor = TXT_WINDOW_BACKGROUND;
     label->fgcolor = TXT_COLOR_BRIGHT_WHITE;
 
     TXT_SetLabel(label, text);
--- a/textscreen/txt_main.h
+++ b/textscreen/txt_main.h
@@ -34,10 +34,12 @@
 
 // Special keypress values that correspond to mouse button clicks
 
-#define TXT_MOUSE_BASE   0x10000
-#define TXT_MOUSE_LEFT   (TXT_MOUSE_BASE + 0)
-#define TXT_MOUSE_RIGHT  (TXT_MOUSE_BASE + 1)
-#define TXT_MOUSE_MIDDLE (TXT_MOUSE_BASE + 2)
+#define TXT_MOUSE_BASE         0x10000
+#define TXT_MOUSE_LEFT         (TXT_MOUSE_BASE + 0)
+#define TXT_MOUSE_RIGHT        (TXT_MOUSE_BASE + 1)
+#define TXT_MOUSE_MIDDLE       (TXT_MOUSE_BASE + 2)
+#define TXT_MOUSE_SCROLLUP     (TXT_MOUSE_BASE + 3)
+#define TXT_MOUSE_SCROLLDOWN   (TXT_MOUSE_BASE + 4)
 #define TXT_MAX_MOUSE_BUTTONS  16
 
 // Screen size
@@ -67,6 +69,16 @@
     TXT_COLOR_BRIGHT_WHITE,
 } txt_color_t;
 
+// Modifier keys.
+
+typedef enum
+{
+    TXT_MOD_SHIFT,
+    TXT_MOD_CTRL,
+    TXT_MOD_ALT,
+    TXT_NUM_MODIFIERS
+} txt_modifier_t;
+
 // Initialize the screen
 // Returns 1 if successful, 0 if failed.
 
@@ -91,6 +103,10 @@
 // Read a character from the keyboard
 
 int TXT_GetChar(void);
+
+// Read the current state of modifier keys that are held down.
+
+int TXT_GetModifierState(txt_modifier_t mod);
 
 // Provides a short description of a key code, placing into the 
 // provided buffer.
--- a/textscreen/txt_radiobutton.c
+++ b/textscreen/txt_radiobutton.c
@@ -48,7 +48,7 @@
 
     w = radiobutton->widget.w;
 
-    TXT_BGColor(TXT_COLOR_BLUE, 0);
+    TXT_BGColor(TXT_WINDOW_BACKGROUND, 0);
     TXT_FGColor(TXT_COLOR_BRIGHT_CYAN);
     TXT_DrawString("(");
 
@@ -67,11 +67,7 @@
 
     TXT_DrawString(") ");
 
-    if (selected)
-    {
-        TXT_BGColor(TXT_COLOR_GREY, 0);
-    }
-
+    TXT_SetWidgetBG(radiobutton, selected);
     TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
 
     TXT_DrawString(radiobutton->label);
--- a/textscreen/txt_scrollpane.c
+++ b/textscreen/txt_scrollpane.c
@@ -416,6 +416,33 @@
 
     scrollbars = NeedsScrollbars(scrollpane);
 
+    if (b == TXT_MOUSE_SCROLLUP)
+    {
+        if (scrollbars & SCROLLBAR_VERTICAL)
+        {
+            scrollpane->y -= 3;
+        }
+        else if (scrollbars & SCROLLBAR_HORIZONTAL)
+        {
+            scrollpane->x -= 3;
+        }
+
+        return;
+    }
+    else if (b == TXT_MOUSE_SCROLLDOWN)
+    {
+        if (scrollbars & SCROLLBAR_VERTICAL)
+        {
+            scrollpane->y += 3;
+        }
+        else if (scrollbars & SCROLLBAR_HORIZONTAL)
+        {
+            scrollpane->x += 3;
+        }
+
+        return;
+    }
+
     rel_x = x - scrollpane->widget.x;
     rel_y = y - scrollpane->widget.y;
 
@@ -433,14 +460,15 @@
         else
         {
             int range = FullWidth(scrollpane) - scrollpane->w;
+            int bar_max = scrollpane->w - 3;
 
-            scrollpane->x = ((rel_x - 1) * range) / (scrollpane->w - 3);
+            scrollpane->x = ((rel_x - 1) * range + (bar_max / 2)) / bar_max;
         }
 
         return;
     }
 
-    // Click on the horizontal scrollbar?
+    // Click on the vertical scrollbar?
     if ((scrollbars & SCROLLBAR_VERTICAL) && rel_x == scrollpane->w)
     {
         if (rel_y == 0)
@@ -454,8 +482,9 @@
         else
         {
             int range = FullHeight(scrollpane) - scrollpane->h;
+            int bar_max = scrollpane->h - 3;
 
-            scrollpane->y = ((rel_y - 1) * range) / (scrollpane->h - 3);
+            scrollpane->y = ((rel_y - 1) * range + (bar_max / 2)) / bar_max;
         }
 
         return;
@@ -465,7 +494,6 @@
     {
         TXT_WidgetMousePress(scrollpane->child, x, y, b);
     }
-
 }
 
 static void TXT_ScrollPaneLayout(TXT_UNCAST_ARG(scrollpane))
@@ -528,6 +556,10 @@
     scrollpane->child = target;
     scrollpane->expand_w = w <= 0;
     scrollpane->expand_h = h <= 0;
+
+    // Set parent pointer for inner widget.
+
+    target->parent = &scrollpane->widget;
 
     return scrollpane;
 }
--- a/textscreen/txt_sdl.c
+++ b/textscreen/txt_sdl.c
@@ -63,6 +63,8 @@
 static TxtSDLEventCallbackFunc event_callback;
 static void *event_callback_data;
 
+static int modifier_state[TXT_NUM_MODIFIERS];
+
 // Font we are using:
 
 static txt_font_t *font;
@@ -225,7 +227,7 @@
 
     // Ignore all mouse motion events
 
-    SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
+//    SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
 
     // Repeat key presses so we can hold down arrows to scroll down the
     // menu, for example. This is what setup.exe does.
@@ -479,6 +481,66 @@
     }
 }
 
+static int MouseHasMoved(void)
+{
+    static int last_x = 0, last_y = 0;
+    int x, y;
+
+    TXT_GetMousePosition(&x, &y);
+
+    if (x != last_x || y != last_y)
+    {
+        last_x = x; last_y = y;
+        return 1;
+    }
+    else
+    {
+        return 0;
+    }
+}
+
+// Examine a key press/release and update the modifier key state
+// if necessary.
+
+static void UpdateModifierState(SDL_keysym *sym, int pressed)
+{
+    txt_modifier_t mod;
+
+    switch (sym->sym)
+    {
+        case SDLK_LSHIFT:
+        case SDLK_RSHIFT:
+            mod = TXT_MOD_SHIFT;
+            break;
+
+        case SDLK_LCTRL:
+        case SDLK_RCTRL:
+            mod = TXT_MOD_CTRL;
+            break;
+
+        case SDLK_LALT:
+        case SDLK_RALT:
+#if !SDL_VERSION_ATLEAST(1, 3, 0)
+        case SDLK_LMETA:
+        case SDLK_RMETA:
+#endif
+            mod = TXT_MOD_ALT;
+            break;
+
+        default:
+            return;
+    }
+
+    if (pressed)
+    {
+        ++modifier_state[mod];
+    }
+    else
+    {
+        --modifier_state[mod];
+    }
+}
+
 signed int TXT_GetChar(void)
 {
     SDL_Event ev;
@@ -508,12 +570,24 @@
                 break;
 
             case SDL_KEYDOWN:
+                UpdateModifierState(&ev.key.keysym, 1);
+
                 return TranslateKey(&ev.key.keysym);
 
+            case SDL_KEYUP:
+                UpdateModifierState(&ev.key.keysym, 0);
+                break;
+
             case SDL_QUIT:
                 // Quit = escape
                 return 27;
 
+            case SDL_MOUSEMOTION:
+                if (MouseHasMoved())
+                {
+                    return 0;
+                }
+
             default:
                 break;
         }
@@ -520,6 +594,16 @@
     }
 
     return -1;
+}
+
+int TXT_GetModifierState(txt_modifier_t mod)
+{
+    if (mod < TXT_NUM_MODIFIERS)
+    {
+        return modifier_state[mod] > 0;
+    }
+
+    return 0;
 }
 
 static const char *SpecialKeyName(int key)
--- a/textscreen/txt_separator.c
+++ b/textscreen/txt_separator.c
@@ -65,7 +65,7 @@
     {
         TXT_GotoXY(x, y);
 
-        TXT_BGColor(TXT_COLOR_BLUE, 0);
+        TXT_BGColor(TXT_WINDOW_BACKGROUND, 0);
         TXT_FGColor(TXT_COLOR_BRIGHT_GREEN);
         TXT_DrawString(" ");
         TXT_DrawString(separator->label);
@@ -80,6 +80,20 @@
     free(separator->label);
 }
 
+void TXT_SetSeparatorLabel(txt_separator_t *separator, char *label)
+{
+    free(separator->label);
+
+    if (label != NULL)
+    {
+        separator->label = strdup(label);
+    }
+    else
+    {
+        separator->label = NULL;
+    }
+}
+
 txt_widget_class_t txt_separator_class =
 {
     TXT_NeverSelectable,
@@ -99,14 +113,8 @@
 
     TXT_InitWidget(separator, &txt_separator_class);
 
-    if (label != NULL)
-    {
-        separator->label = strdup(label);
-    }
-    else
-    {
-        separator->label = NULL;
-    }
+    separator->label = NULL;
+    TXT_SetSeparatorLabel(separator, label);
 
     return separator;
 }
--- a/textscreen/txt_separator.h
+++ b/textscreen/txt_separator.h
@@ -59,6 +59,14 @@
 
 txt_separator_t *TXT_NewSeparator(char *label);
 
-#endif /* #ifndef TXT_SEPARATOR_H */
+/**
+ * Change the label on a separator.
+ *
+ * @param separator     The separator.
+ * @param label         The new label.
+ */
 
+void TXT_SetSeparatorLabel(txt_separator_t *separator, char *label);
+
+#endif /* #ifndef TXT_SEPARATOR_H */
 
--- a/textscreen/txt_spinctrl.c
+++ b/textscreen/txt_spinctrl.c
@@ -149,7 +149,7 @@
     unsigned int padding;
 
     TXT_FGColor(TXT_COLOR_BRIGHT_CYAN);
-    TXT_BGColor(TXT_COLOR_BLUE, 0);
+    TXT_BGColor(TXT_WINDOW_BACKGROUND, 0);
 
     TXT_DrawString("\x1b ");
     
@@ -161,13 +161,9 @@
     {
         TXT_BGColor(TXT_COLOR_BLACK, 0);
     }
-    else if (selected)
-    {
-        TXT_BGColor(TXT_COLOR_GREY, 0);
-    }
     else
     {
-        TXT_BGColor(TXT_COLOR_BLUE, 0);
+        TXT_SetWidgetBG(spincontrol, selected);
     }
 
     if (!spincontrol->editing)
@@ -195,7 +191,7 @@
     }
 
     TXT_FGColor(TXT_COLOR_BRIGHT_CYAN);
-    TXT_BGColor(TXT_COLOR_BLUE, 0);
+    TXT_BGColor(TXT_WINDOW_BACKGROUND, 0);
     TXT_DrawString(" \x1a");
 }
 
--- a/textscreen/txt_table.c
+++ b/textscreen/txt_table.c
@@ -173,6 +173,13 @@
                              sizeof(txt_widget_t *) * (table->num_widgets + 1));
     table->widgets[table->num_widgets] = widget;
     ++table->num_widgets;
+
+    // Maintain parent pointer.
+
+    if (widget != NULL)
+    {
+        widget->parent = &table->widget;
+    }
 }
 
 // Add multiple widgets to a table.
--- a/textscreen/txt_widget.c
+++ b/textscreen/txt_widget.c
@@ -24,6 +24,8 @@
 
 #include "txt_io.h"
 #include "txt_widget.h"
+#include "txt_gui.h"
+#include "txt_desktop.h"
 
 typedef struct
 {
@@ -82,6 +84,7 @@
 
     widget->widget_class = widget_class;
     widget->callback_table = TXT_NewCallbackTable();
+    widget->parent = NULL;
 
     // Visible by default.
 
@@ -234,6 +237,65 @@
     else
     {
         return 0;
+    }
+}
+
+int TXT_ContainsWidget(TXT_UNCAST_ARG(haystack), TXT_UNCAST_ARG(needle))
+{
+    TXT_CAST_ARG(txt_widget_t, haystack);
+    TXT_CAST_ARG(txt_widget_t, needle);
+
+    while (needle != NULL)
+    {
+        if (needle == haystack)
+        {
+            return 1;
+        }
+
+        needle = needle->parent;
+    }
+
+    return 0;
+}
+
+int TXT_HoveringOverWidget(TXT_UNCAST_ARG(widget))
+{
+    TXT_CAST_ARG(txt_widget_t, widget);
+    txt_window_t *active_window;
+    int x, y;
+
+    // We can only be hovering over widgets in the active window.
+
+    active_window = TXT_GetActiveWindow();
+
+    if (active_window == NULL || !TXT_ContainsWidget(active_window, widget))
+    {
+        return 0;
+    }
+
+    // Is the mouse cursor within the bounds of the widget?
+
+    TXT_GetMousePosition(&x, &y);
+
+    return (x >= widget->x && x < widget->x + widget->w
+         && y >= widget->y && y < widget->y + widget->h);
+}
+
+void TXT_SetWidgetBG(TXT_UNCAST_ARG(widget), int selected)
+{
+    TXT_CAST_ARG(txt_widget_t, widget);
+
+    if (selected)
+    {
+        TXT_BGColor(TXT_COLOR_GREY, 0);
+    }
+    else if (TXT_HoveringOverWidget(widget))
+    {
+        TXT_BGColor(TXT_HOVER_BACKGROUND, 0);
+    }
+    else
+    {
+        TXT_BGColor(TXT_WINDOW_BACKGROUND, 0);
     }
 }
 
--- a/textscreen/txt_widget.h
+++ b/textscreen/txt_widget.h
@@ -102,6 +102,10 @@
 
     int x, y;
     unsigned int w, h;
+
+    // Pointer up to parent widget that contains this widget.
+
+    txt_widget_t *parent;
 };
 
 void TXT_InitWidget(TXT_UNCAST_ARG(widget), txt_widget_class_t *widget_class);
@@ -146,6 +150,34 @@
 
 int TXT_SelectableWidget(TXT_UNCAST_ARG(widget));
 
-#endif /* #ifndef TXT_WIDGET_H */
+/**
+ * Query whether the mouse is hovering over the specified widget.
+ *
+ * @param widget       The widget.
+ * @return             Non-zero if the mouse cursor is over the widget.
+ */
 
+int TXT_HoveringOverWidget(TXT_UNCAST_ARG(widget));
+
+/**
+ * Set the background to draw the specified widget, depending on
+ * whether it is selected and the mouse is hovering over it.
+ *
+ * @param widget       The widget.
+ * @param selected     Whether the widget is selected.
+ */
+
+void TXT_SetWidgetBG(TXT_UNCAST_ARG(widget), int selected);
+
+/**
+ * Query whether the specified widget is contained within another
+ * widget.
+ *
+ * @param haystack     The widget that might contain needle.
+ * @param needle       The widget being queried.
+ */
+
+int TXT_ContainsWidget(TXT_UNCAST_ARG(haystack), TXT_UNCAST_ARG(needle));
+
+#endif /* #ifndef TXT_WIDGET_H */
 
--- a/textscreen/txt_window.c
+++ b/textscreen/txt_window.c
@@ -40,6 +40,13 @@
     }
 
     window->actions[position] = action;
+
+    // Maintain parent pointer.
+
+    if (action != NULL)
+    {
+        action->widget.parent = &window->table.widget;
+    }
 }
 
 txt_window_t *TXT_NewWindow(char *title)
@@ -158,7 +165,7 @@
 
         TXT_CalcWidgetSize(widget);
 
-        widget->x = window->window_x + 2;
+        widget->x = window->window_x + 1;
         widget->y = window->window_y + window->window_h - widget->h - 1;
 
         // Adjust available space:
@@ -175,7 +182,7 @@
 
         TXT_CalcWidgetSize(widget);
 
-        widget->x = window->window_x + window->window_w - 2 - widget->w;
+        widget->x = window->window_x + window->window_w - 1 - widget->w;
         widget->y = window->window_y + window->window_h - widget->h - 1;
 
         // Adjust available space:
@@ -220,7 +227,7 @@
     txt_widget_t *widget;
     int i;
 
-    *w = 1;
+    *w = 0;
     *h = 0;
 
     // Calculate the width of all the action widgets and use this
@@ -233,7 +240,7 @@
         if (widget != NULL)
         {
             TXT_CalcWidgetSize(widget);
-            *w += widget->w + 1;
+            *w += widget->w;
 
             if (widget->h > *h)
             {
--- a/textscreen/txt_window_action.c
+++ b/textscreen/txt_window_action.c
@@ -37,9 +37,10 @@
 
     TXT_GetKeyDescription(action->key, buf);
 
-    // Minimum width is the string length + two spaces for padding
+    // Width is label length, plus key description length, plus '='
+    // and two surrounding spaces.
 
-    action->widget.w = strlen(action->label) + strlen(buf) + 1;
+    action->widget.w = strlen(action->label) + strlen(buf) + 3;
     action->widget.h = 1;
 }
 
@@ -50,12 +51,24 @@
 
     TXT_GetKeyDescription(action->key, buf);
 
+    if (TXT_HoveringOverWidget(action))
+    {
+        TXT_BGColor(TXT_COLOR_BLACK, 0);
+    }
+    else
+    {
+        TXT_BGColor(TXT_WINDOW_BACKGROUND, 0);
+    }
+
+    TXT_DrawString(" ");
     TXT_FGColor(TXT_COLOR_BRIGHT_GREEN);
     TXT_DrawString(buf);
     TXT_FGColor(TXT_COLOR_BRIGHT_CYAN);
     TXT_DrawString("=");
+
     TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
     TXT_DrawString(action->label);
+    TXT_DrawString(" ");
 }
 
 static void TXT_WindowActionDestructor(TXT_UNCAST_ARG(action))
@@ -69,7 +82,7 @@
 {
     TXT_CAST_ARG(txt_window_action_t, action);
 
-    if (key == action->key)
+    if (tolower(key) == tolower(action->key))
     {
         TXT_EmitSignal(action, "pressed");
         return 1;