shithub: qk2

Download patch

ref: c05151aa1b7ac81866cf937c973e7038035dfe53
parent: 005cceb68e49b40e29120a94e9e7138a54f5e65c
author: Konstantinn Bonnet <qu7uux@gmail.com>
date: Fri Feb 27 19:20:40 EST 2015

copy stuff over from linux for plan9

--- /dev/null
+++ b/plan9/cd_linux.c
@@ -1,0 +1,420 @@
+// Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All
+// rights reserved.
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#include <linux/cdrom.h>
+
+#include "../client/client.h"
+
+static qboolean cdValid = false;
+static qboolean	playing = false;
+static qboolean	wasPlaying = false;
+static qboolean	initialized = false;
+static qboolean	enabled = true;
+static qboolean playLooping = false;
+static float	cdvolume;
+static byte 	remap[100];
+static byte		playTrack;
+static byte		maxTrack;
+
+static int cdfile = -1;
+
+//static char cd_dev[64] = "/dev/cdrom";
+
+cvar_t	*cd_volume;
+cvar_t *cd_nocd;
+cvar_t *cd_dev;
+
+void CDAudio_Pause(void);
+
+static void CDAudio_Eject(void)
+{
+	if (cdfile == -1 || !enabled)
+		return; // no cd init'd
+
+	if ( ioctl(cdfile, CDROMEJECT) == -1 ) 
+		Com_DPrintf("ioctl cdromeject failed\n");
+}
+
+
+static void CDAudio_CloseDoor(void)
+{
+	if (cdfile == -1 || !enabled)
+		return; // no cd init'd
+
+	if ( ioctl(cdfile, CDROMCLOSETRAY) == -1 ) 
+		Com_DPrintf("ioctl cdromclosetray failed\n");
+}
+
+static int CDAudio_GetAudioDiskInfo(void)
+{
+	struct cdrom_tochdr tochdr;
+
+	cdValid = false;
+
+	if ( ioctl(cdfile, CDROMREADTOCHDR, &tochdr) == -1 ) 
+    {
+      Com_DPrintf("ioctl cdromreadtochdr failed\n");
+	  return -1;
+    }
+
+	if (tochdr.cdth_trk0 < 1)
+	{
+		Com_DPrintf("CDAudio: no music tracks\n");
+		return -1;
+	}
+
+	cdValid = true;
+	maxTrack = tochdr.cdth_trk1;
+
+	return 0;
+}
+
+
+void CDAudio_Play(int track, qboolean looping)
+{
+	struct cdrom_tocentry entry;
+	struct cdrom_ti ti;
+
+	if (cdfile == -1 || !enabled)
+		return;
+	
+	if (!cdValid)
+	{
+		CDAudio_GetAudioDiskInfo();
+		if (!cdValid)
+			return;
+	}
+
+	track = remap[track];
+
+	if (track < 1 || track > maxTrack)
+	{
+		Com_DPrintf("CDAudio: Bad track number %u.\n", track);
+		return;
+	}
+
+	// don't try to play a non-audio track
+	entry.cdte_track = track;
+	entry.cdte_format = CDROM_MSF;
+    if ( ioctl(cdfile, CDROMREADTOCENTRY, &entry) == -1 )
+	{
+		Com_DPrintf("ioctl cdromreadtocentry failed\n");
+		return;
+	}
+	if (entry.cdte_ctrl == CDROM_DATA_TRACK)
+	{
+		Com_Printf("CDAudio: track %i is not audio\n", track);
+		return;
+	}
+
+	if (playing)
+	{
+		if (playTrack == track)
+			return;
+		CDAudio_Stop();
+	}
+
+	ti.cdti_trk0 = track;
+	ti.cdti_trk1 = track;
+	ti.cdti_ind0 = 1;
+	ti.cdti_ind1 = 99;
+
+	if ( ioctl(cdfile, CDROMPLAYTRKIND, &ti) == -1 ) 
+    {
+		Com_DPrintf("ioctl cdromplaytrkind failed\n");
+		return;
+    }
+
+	if ( ioctl(cdfile, CDROMRESUME) == -1 ) 
+		Com_DPrintf("ioctl cdromresume failed\n");
+
+	playLooping = looping;
+	playTrack = track;
+	playing = true;
+
+	if (cd_volume->value == 0.0)
+		CDAudio_Pause ();
+}
+
+
+void CDAudio_Stop(void)
+{
+	if (cdfile == -1 || !enabled)
+		return;
+	
+	if (!playing)
+		return;
+
+	if ( ioctl(cdfile, CDROMSTOP) == -1 )
+		Com_DPrintf("ioctl cdromstop failed (%d)\n", errno);
+
+	wasPlaying = false;
+	playing = false;
+}
+
+void CDAudio_Pause(void)
+{
+	if (cdfile == -1 || !enabled)
+		return;
+
+	if (!playing)
+		return;
+
+	if ( ioctl(cdfile, CDROMPAUSE) == -1 ) 
+		Com_DPrintf("ioctl cdrompause failed\n");
+
+	wasPlaying = playing;
+	playing = false;
+}
+
+
+void CDAudio_Resume(void)
+{
+	if (cdfile == -1 || !enabled)
+		return;
+	
+	if (!cdValid)
+		return;
+
+	if (!wasPlaying)
+		return;
+	
+	if ( ioctl(cdfile, CDROMRESUME) == -1 ) 
+		Com_DPrintf("ioctl cdromresume failed\n");
+	playing = true;
+}
+
+static void CD_f (void)
+{
+	char	*command;
+	int		ret;
+	int		n;
+
+	if (Cmd_Argc() < 2)
+		return;
+
+	command = Cmd_Argv (1);
+
+	if (Q_strcasecmp(command, "on") == 0)
+	{
+		enabled = true;
+		return;
+	}
+
+	if (Q_strcasecmp(command, "off") == 0)
+	{
+		if (playing)
+			CDAudio_Stop();
+		enabled = false;
+		return;
+	}
+
+	if (Q_strcasecmp(command, "reset") == 0)
+	{
+		enabled = true;
+		if (playing)
+			CDAudio_Stop();
+		for (n = 0; n < 100; n++)
+			remap[n] = n;
+		CDAudio_GetAudioDiskInfo();
+		return;
+	}
+
+	if (Q_strcasecmp(command, "remap") == 0)
+	{
+		ret = Cmd_Argc() - 2;
+		if (ret <= 0)
+		{
+			for (n = 1; n < 100; n++)
+				if (remap[n] != n)
+					Com_Printf("  %u -> %u\n", n, remap[n]);
+			return;
+		}
+		for (n = 1; n <= ret; n++)
+			remap[n] = atoi(Cmd_Argv (n+1));
+		return;
+	}
+
+	if (Q_strcasecmp(command, "close") == 0)
+	{
+		CDAudio_CloseDoor();
+		return;
+	}
+
+	if (!cdValid)
+	{
+		CDAudio_GetAudioDiskInfo();
+		if (!cdValid)
+		{
+			Com_Printf("No CD in player.\n");
+			return;
+		}
+	}
+
+	if (Q_strcasecmp(command, "play") == 0)
+	{
+		CDAudio_Play((byte)atoi(Cmd_Argv (2)), false);
+		return;
+	}
+
+	if (Q_strcasecmp(command, "loop") == 0)
+	{
+		CDAudio_Play((byte)atoi(Cmd_Argv (2)), true);
+		return;
+	}
+
+	if (Q_strcasecmp(command, "stop") == 0)
+	{
+		CDAudio_Stop();
+		return;
+	}
+
+	if (Q_strcasecmp(command, "pause") == 0)
+	{
+		CDAudio_Pause();
+		return;
+	}
+
+	if (Q_strcasecmp(command, "resume") == 0)
+	{
+		CDAudio_Resume();
+		return;
+	}
+
+	if (Q_strcasecmp(command, "eject") == 0)
+	{
+		if (playing)
+			CDAudio_Stop();
+		CDAudio_Eject();
+		cdValid = false;
+		return;
+	}
+
+	if (Q_strcasecmp(command, "info") == 0)
+	{
+		Com_Printf("%u tracks\n", maxTrack);
+		if (playing)
+			Com_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack);
+		else if (wasPlaying)
+			Com_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack);
+		Com_Printf("Volume is %f\n", cdvolume);
+		return;
+	}
+}
+
+void CDAudio_Update(void)
+{
+	struct cdrom_subchnl subchnl;
+	static time_t lastchk;
+
+	if (cdfile == -1 || !enabled)
+		return;
+
+	if (cd_volume && cd_volume->value != cdvolume)
+	{
+		if (cdvolume)
+		{
+			Cvar_SetValue ("cd_volume", 0.0);
+			cdvolume = cd_volume->value;
+			CDAudio_Pause ();
+		}
+		else
+		{
+			Cvar_SetValue ("cd_volume", 1.0);
+			cdvolume = cd_volume->value;
+			CDAudio_Resume ();
+		}
+	}
+
+	if (playing && lastchk < time(NULL)) {
+		lastchk = time(NULL) + 2; //two seconds between chks
+		subchnl.cdsc_format = CDROM_MSF;
+		if (ioctl(cdfile, CDROMSUBCHNL, &subchnl) == -1 ) {
+			Com_DPrintf("ioctl cdromsubchnl failed\n");
+			playing = false;
+			return;
+		}
+		if (subchnl.cdsc_audiostatus != CDROM_AUDIO_PLAY &&
+			subchnl.cdsc_audiostatus != CDROM_AUDIO_PAUSED) {
+			playing = false;
+			if (playLooping)
+				CDAudio_Play(playTrack, true);
+		}
+	}
+}
+
+int CDAudio_Init(void)
+{
+	int i;
+	cvar_t			*cv;
+	extern uid_t saved_euid;
+
+	cv = Cvar_Get ("nocdaudio", "0", CVAR_NOSET);
+	if (cv->value)
+		return -1;
+
+	cd_nocd = Cvar_Get ("cd_nocd", "0", CVAR_ARCHIVE );
+	if ( cd_nocd->value)
+		return -1;
+
+	cd_volume = Cvar_Get ("cd_volume", "1", CVAR_ARCHIVE);
+
+	cd_dev = Cvar_Get("cd_dev", "/dev/cdrom", CVAR_ARCHIVE);
+
+	seteuid(saved_euid);
+
+	cdfile = open(cd_dev->string, O_RDONLY);
+
+	seteuid(getuid());
+
+	if (cdfile == -1) {
+		Com_Printf("CDAudio_Init: open of \"%s\" failed (%i)\n", cd_dev->string, errno);
+		cdfile = -1;
+		return -1;
+	}
+
+	for (i = 0; i < 100; i++)
+		remap[i] = i;
+	initialized = true;
+	enabled = true;
+
+	if (CDAudio_GetAudioDiskInfo())
+	{
+		Com_Printf("CDAudio_Init: No CD in player.\n");
+		cdValid = false;
+	}
+
+	Cmd_AddCommand ("cd", CD_f);
+
+	Com_Printf("CD Audio Initialized\n");
+
+	return 0;
+}
+
+void CDAudio_Activate (qboolean active)
+{
+	if (active)
+		CDAudio_Resume ();
+	else
+		CDAudio_Pause ();
+}
+
+void CDAudio_Shutdown(void)
+{
+	if (!initialized)
+		return;
+	CDAudio_Stop();
+	close(cdfile);
+	cdfile = -1;
+}
--- /dev/null
+++ b/plan9/glob.c
@@ -1,0 +1,164 @@
+
+#include <stdio.h>
+#include "../linux/glob.h"
+
+/* Like glob_match, but match PATTERN against any final segment of TEXT.  */
+static int glob_match_after_star(char *pattern, char *text)
+{
+	register char *p = pattern, *t = text;
+	register char c, c1;
+
+	while ((c = *p++) == '?' || c == '*')
+		if (c == '?' && *t++ == '\0')
+			return 0;
+
+	if (c == '\0')
+		return 1;
+
+	if (c == '\\')
+		c1 = *p;
+	else
+		c1 = c;
+
+	while (1) {
+		if ((c == '[' || *t == c1) && glob_match(p - 1, t))
+			return 1;
+		if (*t++ == '\0')
+			return 0;
+	}
+}
+
+/* Return nonzero if PATTERN has any special globbing chars in it.  */
+static int glob_pattern_p(char *pattern)
+{
+	register char *p = pattern;
+	register char c;
+	int open = 0;
+
+	while ((c = *p++) != '\0')
+		switch (c) {
+		case '?':
+		case '*':
+			return 1;
+
+		case '[':		/* Only accept an open brace if there is a close */
+			open++;		/* brace to match it.  Bracket expressions must be */
+			continue;	/* complete, according to Posix.2 */
+		case ']':
+			if (open)
+				return 1;
+			continue;
+
+		case '\\':
+			if (*p++ == '\0')
+				return 0;
+		}
+
+	return 0;
+}
+
+/* Match the pattern PATTERN against the string TEXT;
+   return 1 if it matches, 0 otherwise.
+
+   A match means the entire string TEXT is used up in matching.
+
+   In the pattern string, `*' matches any sequence of characters,
+   `?' matches any character, [SET] matches any character in the specified set,
+   [!SET] matches any character not in the specified set.
+
+   A set is composed of characters or ranges; a range looks like
+   character hyphen character (as in 0-9 or A-Z).
+   [0-9a-zA-Z_] is the set of characters allowed in C identifiers.
+   Any other character in the pattern must be matched exactly.
+
+   To suppress the special syntactic significance of any of `[]*?!-\',
+   and match the character exactly, precede it with a `\'.
+*/
+
+int glob_match(char *pattern, char *text)
+{
+	register char *p = pattern, *t = text;
+	register char c;
+
+	while ((c = *p++) != '\0')
+		switch (c) {
+		case '?':
+			if (*t == '\0')
+				return 0;
+			else
+				++t;
+			break;
+
+		case '\\':
+			if (*p++ != *t++)
+				return 0;
+			break;
+
+		case '*':
+			return glob_match_after_star(p, t);
+
+		case '[':
+			{
+				register char c1 = *t++;
+				int invert;
+
+				if (!c1)
+					return (0);
+
+				invert = ((*p == '!') || (*p == '^'));
+				if (invert)
+					p++;
+
+				c = *p++;
+				while (1) {
+					register char cstart = c, cend = c;
+
+					if (c == '\\') {
+						cstart = *p++;
+						cend = cstart;
+					}
+					if (c == '\0')
+						return 0;
+
+					c = *p++;
+					if (c == '-' && *p != ']') {
+						cend = *p++;
+						if (cend == '\\')
+							cend = *p++;
+						if (cend == '\0')
+							return 0;
+						c = *p++;
+					}
+					if (c1 >= cstart && c1 <= cend)
+						goto match;
+					if (c == ']')
+						break;
+				}
+				if (!invert)
+					return 0;
+				break;
+
+			  match:
+				/* Skip the rest of the [...] construct that already matched.  */
+				while (c != ']') {
+					if (c == '\0')
+						return 0;
+					c = *p++;
+					if (c == '\0')
+						return 0;
+					else if (c == '\\')
+						++p;
+				}
+				if (invert)
+					return 0;
+				break;
+			}
+
+		default:
+			if (c != *t++)
+				return 0;
+		}
+
+	return *t == '\0';
+}
+
--- /dev/null
+++ b/plan9/glob.h
@@ -1,0 +1,1 @@
+int glob_match(char *pattern, char *text);
--- /dev/null
+++ b/plan9/in_linux.c
@@ -1,0 +1,29 @@
+// in_null.c -- for systems without a mouse
+
+#include "../client/client.h"
+
+cvar_t	*in_mouse;
+cvar_t	*in_joystick;
+
+void IN_Init (void)
+{
+    in_mouse = Cvar_Get ("in_mouse", "1", CVAR_ARCHIVE);
+    in_joystick = Cvar_Get ("in_joystick", "0", CVAR_ARCHIVE);
+}
+
+void IN_Shutdown (void)
+{
+}
+
+void IN_Commands (void)
+{
+}
+
+void IN_Move (usercmd_t *cmd)
+{
+}
+
+void IN_Activate (qboolean active)
+{
+}
+
--- /dev/null
+++ b/plan9/mkfile
@@ -1,0 +1,299 @@
+</$objtype/mkfile
+
+BIN=.
+TARG=qk2
+
+CLDIR=../client
+SVDIR=../server
+SRDIR=../ref_soft
+CMDIR=../qcommon
+P9DIR=.
+GMDIR=../game
+CTFDIR=../ctf
+XADIR=../xatrix
+
+CLOFILES=\
+	$CLDIR/cl_cin.$O\
+	$CLDIR/cl_ents.$O\
+	$CLDIR/cl_fx.$O\
+	$CLDIR/cl_newfx.$O\
+	$CLDIR/cl_input.$O\
+	$CLDIR/cl_inv.$O\
+	$CLDIR/cl_main.$O\
+	$CLDIR/cl_parse.$O\
+	$CLDIR/cl_pred.$O\
+	$CLDIR/cl_tent.$O\
+	$CLDIR/cl_scrn.$O\
+	$CLDIR/cl_view.$O\
+	$CLDIR/console.$O\
+	$CLDIR/keys.$O\
+	$CLDIR/menu.$O\
+	$CLDIR/snd_dma.$O\
+	$CLDIR/snd_mem.$O\
+	$CLDIR/snd_mix.$O\
+	$CLDIR/qmenu.$O\
+	$GMDIR/m_flash.$O\
+	$CMDIR/cmd.$O\
+	$CMDIR/cmodel.$O\
+	$CMDIR/common.$O\
+	$CMDIR/crc.$O\
+	$CMDIR/cvar.$O\
+	$CMDIR/files.$O\
+	$CMDIR/md4.$O\
+	$CMDIR/net_chan.$O\
+	$GMDIR/q_shared.$O\
+	$CMDIR/pmove.$O\
+	$SVDIR/sv_ccmds.$O\
+	$SVDIR/sv_ents.$O\
+	$SVDIR/sv_game.$O\
+	$SVDIR/sv_init.$O\
+	$SVDIR/sv_main.$O\
+	$SVDIR/sv_send.$O\
+	$SVDIR/sv_user.$O\
+	$SVDIR/sv_world.$O\
+	$P9DIR/cd_linux.$O\
+	$P9DIR/q_shlinux.$O\
+	$P9DIR/vid_menu.$O\
+	$P9DIR/vid_so.$O\
+	$P9DIR/snd_linux.$O\
+	$P9DIR/sys_linux.$O\
+	$P9DIR/glob.$O\
+	$P9DIR/net_udp.$O\
+
+CLHFILES=\
+	$CLDIR/adivtab.h\
+	$CLDIR/anorms.h\
+	$CLDIR/asm_i386.h\
+	$CLDIR/block16.h\
+	$CLDIR/block8.h\
+	$CLDIR/cdaudio.h\
+	$CLDIR/client.h\
+	$CLDIR/console.h\
+	$CLDIR/input.h\
+	$CLDIR/keys.h\
+	$CLDIR/qmenu.h\
+	$CLDIR/ref.h\
+	$CLDIR/screen.h\
+	$CLDIR/snd_loc.h\
+	$CLDIR/sound.h\
+	$CLDIR/vid.h\
+
+# g_chase.c -> ChasePrev, ChaseNext, .. undefined symbols -> dlopen fails
+	#$GMDIR/g_chase.$O\
+GMOFILES=\
+	$GMDIR/g_ai.$O\
+	$GMDIR/p_client.$O\
+	$GMDIR/g_cmds.$O\
+	$GMDIR/g_svcmds.$O\
+	$GMDIR/g_combat.$O\
+	$GMDIR/g_func.$O\
+	$GMDIR/g_items.$O\
+	$GMDIR/g_main.$O\
+	$GMDIR/g_misc.$O\
+	$GMDIR/g_monster.$O\
+	$GMDIR/g_phys.$O\
+	$GMDIR/g_save.$O\
+	$GMDIR/g_spawn.$O\
+	$GMDIR/g_target.$O\
+	$GMDIR/g_trigger.$O\
+	$GMDIR/g_turret.$O\
+	$GMDIR/g_utils.$O\
+	$GMDIR/g_weapon.$O\
+	$GMDIR/m_actor.$O\
+	$GMDIR/m_berserk.$O\
+	$GMDIR/m_boss2.$O\
+	$GMDIR/m_boss3.$O\
+	$GMDIR/m_boss31.$O\
+	$GMDIR/m_boss32.$O\
+	$GMDIR/m_brain.$O\
+	$GMDIR/m_chick.$O\
+	$GMDIR/m_flipper.$O\
+	$GMDIR/m_float.$O\
+	$GMDIR/m_flyer.$O\
+	$GMDIR/m_gladiator.$O\
+	$GMDIR/m_gunner.$O\
+	$GMDIR/m_hover.$O\
+	$GMDIR/m_infantry.$O\
+	$GMDIR/m_insane.$O\
+	$GMDIR/m_medic.$O\
+	$GMDIR/m_move.$O\
+	$GMDIR/m_mutant.$O\
+	$GMDIR/m_parasite.$O\
+	$GMDIR/m_soldier.$O\
+	$GMDIR/m_supertank.$O\
+	$GMDIR/m_tank.$O\
+	$GMDIR/p_hud.$O\
+	$GMDIR/p_trail.$O\
+	$GMDIR/p_view.$O\
+	$GMDIR/p_weapon.$O\
+	$GMDIR/q_shared.$O\
+	$GMDIR/m_flash.$O\
+
+GMHFILES=\
+	$GMDIR/g_local.h\
+	$GMDIR/game.h\
+	$GMDIR/m_actor.h\
+	$GMDIR/m_berserk.h\
+	$GMDIR/m_boss2.h\
+	$GMDIR/m_boss31.h\
+	$GMDIR/m_boss32.h\
+	$GMDIR/m_brain.h\
+	$GMDIR/m_chick.h\
+	$GMDIR/m_flipper.h\
+	$GMDIR/m_float.h\
+	$GMDIR/m_flyer.h\
+	$GMDIR/m_gladiator.h\
+	$GMDIR/m_gunner.h\
+	$GMDIR/m_hover.h\
+	$GMDIR/m_infantry.h\
+	$GMDIR/m_insane.h\
+	$GMDIR/m_medic.h\
+	$GMDIR/m_mutant.h\
+	$GMDIR/m_parasite.h\
+	$GMDIR/m_player.h\
+	$GMDIR/m_rider.h\
+	$GMDIR/m_soldier.h\
+	$GMDIR/m_supertank.h\
+	$GMDIR/m_tank.h\
+	$GMDIR/q_shared.h\
+
+CTFOFILES=\
+	$CTFDIR/g_ai.$O\
+	$CTFDIR/g_chase.$O\
+	$CTFDIR/g_cmds.$O\
+	$CTFDIR/g_combat.$O\
+	$CTFDIR/g_ctf.$O\
+	$CTFDIR/g_func.$O\
+	$CTFDIR/g_items.$O\
+	$CTFDIR/g_main.$O\
+	$CTFDIR/g_misc.$O\
+	$CTFDIR/g_monster.$O\
+	$CTFDIR/g_phys.$O\
+	$CTFDIR/g_save.$O\
+	$CTFDIR/g_spawn.$O\
+	$CTFDIR/g_svcmds.$O\
+	$CTFDIR/g_target.$O\
+	$CTFDIR/g_trigger.$O\
+	$CTFDIR/g_utils.$O\
+	$CTFDIR/g_weapon.$O\
+	$CTFDIR/m_move.$O\
+	$CTFDIR/p_client.$O\
+	$CTFDIR/p_hud.$O\
+	$CTFDIR/p_menu.$O\
+	$CTFDIR/p_trail.$O\
+	$CTFDIR/p_view.$O\
+	$CTFDIR/p_weapon.$O\
+	$CTFDIR/q_shared.$O\
+
+CTFHFILES=\
+	$CTFDIR/g_ctf.h\
+	$CTFDIR/g_local.h\
+	$CTFDIR/game.h\
+	$CTFDIR/m_player.h\
+	$CTFDIR/p_menu.h\
+	$CTFDIR/q_shared.h\
+
+XAOFILES=\
+	$XADIR/g_ai.$O\
+	$XADIR/g_cmds.$O\
+	$XADIR/g_combat.$O\
+	$XADIR/g_func.$O\
+	$XADIR/g_items.$O\
+	$XADIR/g_main.$O\
+	$XADIR/g_misc.$O\
+	$XADIR/g_monster.$O\
+	$XADIR/g_phys.$O\
+	$XADIR/g_save.$O\
+	$XADIR/g_spawn.$O\
+	$XADIR/g_svcmds.$O\
+	$XADIR/g_target.$O\
+	$XADIR/g_trigger.$O\
+	$XADIR/g_turret.$O\
+	$XADIR/g_utils.$O\
+	$XADIR/g_weapon.$O\
+	$XADIR/m_actor.$O\
+	$XADIR/m_berserk.$O\
+	$XADIR/m_boss2.$O\
+	$XADIR/m_boss3.$O\
+	$XADIR/m_boss31.$O\
+	$XADIR/m_boss32.$O\
+	$XADIR/m_boss5.$O\
+	$XADIR/m_brain.$O\
+	$XADIR/m_chick.$O\
+	$XADIR/m_fixbot.$O\
+	$XADIR/m_flash.$O\
+	$XADIR/m_flipper.$O\
+	$XADIR/m_float.$O\
+	$XADIR/m_flyer.$O\
+	$XADIR/m_gekk.$O\
+	$XADIR/m_gladb.$O\
+	$XADIR/m_gladiator.$O\
+	$XADIR/m_gunner.$O\
+	$XADIR/m_hover.$O\
+	$XADIR/m_infantry.$O\
+	$XADIR/m_insane.$O\
+	$XADIR/m_medic.$O\
+	$XADIR/m_move.$O\
+	$XADIR/m_mutant.$O\
+	$XADIR/m_parasite.$O\
+	$XADIR/m_soldier.$O\
+	$XADIR/m_supertank.$O\
+	$XADIR/m_tank.$O\
+	$XADIR/p_client.$O\
+	$XADIR/p_hud.$O\
+	$XADIR/p_trail.$O\
+	$XADIR/p_view.$O\
+	$XADIR/p_weapon.$O\
+	$XADIR/q_shared.$O\
+
+# FIXME
+XAHFILES=
+
+SROFILES=\
+	$SRDIR/r_aclip.$O\
+	$SRDIR/r_alias.$O\
+	$SRDIR/r_bsp.$O\
+	$SRDIR/r_draw.$O\
+	$SRDIR/r_edge.$O\
+	$SRDIR/r_image.$O\
+	$SRDIR/r_light.$O\
+	$SRDIR/r_main.$O\
+	$SRDIR/r_misc.$O\
+	$SRDIR/r_model.$O\
+	$SRDIR/r_part.$O\
+	$SRDIR/r_poly.$O\
+	$SRDIR/r_polyse.$O\
+	$SRDIR/r_rast.$O\
+	$SRDIR/r_scan.$O\
+	$SRDIR/r_sprite.$O\
+	$SRDIR/r_surf.$O\
+	$GMDIR/q_shared.$O\
+	$P9DIR/q_shlinux.$O\
+	$P9DIR/glob.$O\
+	$P9DIR/rw_x11.$O\
+
+SRHFILES=\
+	$SRDIR/adivtab.h\
+	$SRDIR/anorms.h\
+	$SRDIR/asm_draw.h\
+	$SRDIR/d_ifacea.h\
+	$SRDIR/r_local.h\
+	$SRDIR/r_model.h\
+	$SRDIR/rand1k.h\
+
+CMHFILES=\
+	$CMDIR/crc.h\
+	$CMDIR/qcommon.h\
+	$CMDIR/qfiles.h\
+
+# only build and link needed modules for main game
+# *ofiles: ofiles needed for each module
+# *hfiles: hfiles in each module directory...
+OFILES= $CLOFILES $GMOFILES $SROFILES
+HFILES= $CLHFILES $GMHFILES $SRHFILES $CMHFILES
+
+</sys/src/cmd/mkone
+
+# FIXME
+clean:
+	rm -f $CLOFILES $GMOFILES $SROFILES $CTFOFILES $XAOFILES
--- /dev/null
+++ b/plan9/net_udp.c
@@ -1,0 +1,537 @@
+// net_wins.c
+
+#include "../qcommon/qcommon.h"
+
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <errno.h>
+
+#ifdef NeXT
+#include <libc.h>
+#endif
+
+netadr_t	net_local_adr;
+
+#define	LOOPBACK	0x7f000001
+
+#define	MAX_LOOPBACK	4
+
+typedef struct
+{
+	byte	data[MAX_MSGLEN];
+	int		datalen;
+} loopmsg_t;
+
+typedef struct
+{
+	loopmsg_t	msgs[MAX_LOOPBACK];
+	int			get, send;
+} loopback_t;
+
+loopback_t	loopbacks[2];
+int			ip_sockets[2];
+int			ipx_sockets[2];
+
+int NET_Socket (char *net_interface, int port);
+char *NET_ErrorString (void);
+
+//=============================================================================
+
+void NetadrToSockadr (netadr_t *a, struct sockaddr_in *s)
+{
+	memset (s, 0, sizeof(*s));
+
+	if (a->type == NA_BROADCAST)
+	{
+		s->sin_family = AF_INET;
+
+		s->sin_port = a->port;
+		*(int *)&s->sin_addr = -1;
+	}
+	else if (a->type == NA_IP)
+	{
+		s->sin_family = AF_INET;
+
+		*(int *)&s->sin_addr = *(int *)&a->ip;
+		s->sin_port = a->port;
+	}
+}
+
+void SockadrToNetadr (struct sockaddr_in *s, netadr_t *a)
+{
+	*(int *)&a->ip = *(int *)&s->sin_addr;
+	a->port = s->sin_port;
+	a->type = NA_IP;
+}
+
+
+qboolean	NET_CompareAdr (netadr_t a, netadr_t b)
+{
+	if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3] && a.port == b.port)
+		return true;
+	return false;
+}
+
+/*
+===================
+NET_CompareBaseAdr
+
+Compares without the port
+===================
+*/
+qboolean	NET_CompareBaseAdr (netadr_t a, netadr_t b)
+{
+	if (a.type != b.type)
+		return false;
+
+	if (a.type == NA_LOOPBACK)
+		return true;
+
+	if (a.type == NA_IP)
+	{
+		if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3])
+			return true;
+		return false;
+	}
+
+	if (a.type == NA_IPX)
+	{
+		if ((memcmp(a.ipx, b.ipx, 10) == 0))
+			return true;
+		return false;
+	}
+}
+
+char	*NET_AdrToString (netadr_t a)
+{
+	static	char	s[64];
+	
+	Com_sprintf (s, sizeof(s), "%i.%i.%i.%i:%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3], ntohs(a.port));
+
+	return s;
+}
+
+char	*NET_BaseAdrToString (netadr_t a)
+{
+	static	char	s[64];
+	
+	Com_sprintf (s, sizeof(s), "%i.%i.%i.%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3]);
+
+	return s;
+}
+
+/*
+=============
+NET_StringToAdr
+
+localhost
+idnewt
+idnewt:28000
+192.246.40.70
+192.246.40.70:28000
+=============
+*/
+qboolean	NET_StringToSockaddr (char *s, struct sockaddr *sadr)
+{
+	struct hostent	*h;
+	char	*colon;
+	char	copy[128];
+	
+	memset (sadr, 0, sizeof(*sadr));
+	((struct sockaddr_in *)sadr)->sin_family = AF_INET;
+	
+	((struct sockaddr_in *)sadr)->sin_port = 0;
+
+	strcpy (copy, s);
+	// strip off a trailing :port if present
+	for (colon = copy ; *colon ; colon++)
+		if (*colon == ':')
+		{
+			*colon = 0;
+			((struct sockaddr_in *)sadr)->sin_port = htons((short)atoi(colon+1));	
+		}
+	
+	if (copy[0] >= '0' && copy[0] <= '9')
+	{
+		*(int *)&((struct sockaddr_in *)sadr)->sin_addr = inet_addr(copy);
+	}
+	else
+	{
+		if (! (h = gethostbyname(copy)) )
+			return 0;
+		*(int *)&((struct sockaddr_in *)sadr)->sin_addr = *(int *)h->h_addr_list[0];
+	}
+	
+	return true;
+}
+
+/*
+=============
+NET_StringToAdr
+
+localhost
+idnewt
+idnewt:28000
+192.246.40.70
+192.246.40.70:28000
+=============
+*/
+qboolean	NET_StringToAdr (char *s, netadr_t *a)
+{
+	struct sockaddr_in sadr;
+	
+	if (!strcmp (s, "localhost"))
+	{
+		memset (a, 0, sizeof(*a));
+		a->type = NA_LOOPBACK;
+		return true;
+	}
+
+	if (!NET_StringToSockaddr (s, (struct sockaddr *)&sadr))
+		return false;
+	
+	SockadrToNetadr (&sadr, a);
+
+	return true;
+}
+
+
+qboolean	NET_IsLocalAddress (netadr_t adr)
+{
+	return NET_CompareAdr (adr, net_local_adr);
+}
+
+/*
+=============================================================================
+
+LOOPBACK BUFFERS FOR LOCAL PLAYER
+
+=============================================================================
+*/
+
+qboolean	NET_GetLoopPacket (netsrc_t sock, netadr_t *net_from, sizebuf_t *net_message)
+{
+	int		i;
+	loopback_t	*loop;
+
+	loop = &loopbacks[sock];
+
+	if (loop->send - loop->get > MAX_LOOPBACK)
+		loop->get = loop->send - MAX_LOOPBACK;
+
+	if (loop->get >= loop->send)
+		return false;
+
+	i = loop->get & (MAX_LOOPBACK-1);
+	loop->get++;
+
+	memcpy (net_message->data, loop->msgs[i].data, loop->msgs[i].datalen);
+	net_message->cursize = loop->msgs[i].datalen;
+	*net_from = net_local_adr;
+	return true;
+
+}
+
+
+void NET_SendLoopPacket (netsrc_t sock, int length, void *data, netadr_t to)
+{
+	int		i;
+	loopback_t	*loop;
+
+	loop = &loopbacks[sock^1];
+
+	i = loop->send & (MAX_LOOPBACK-1);
+	loop->send++;
+
+	memcpy (loop->msgs[i].data, data, length);
+	loop->msgs[i].datalen = length;
+}
+
+//=============================================================================
+
+qboolean	NET_GetPacket (netsrc_t sock, netadr_t *net_from, sizebuf_t *net_message)
+{
+	int 	ret;
+	struct sockaddr_in	from;
+	int		fromlen;
+	int		net_socket;
+	int		protocol;
+	int		err;
+
+	if (NET_GetLoopPacket (sock, net_from, net_message))
+		return true;
+
+	for (protocol = 0 ; protocol < 2 ; protocol++)
+	{
+		if (protocol == 0)
+			net_socket = ip_sockets[sock];
+		else
+			net_socket = ipx_sockets[sock];
+
+		if (!net_socket)
+			continue;
+
+		fromlen = sizeof(from);
+		ret = recvfrom (net_socket, net_message->data, net_message->maxsize
+			, 0, (struct sockaddr *)&from, &fromlen);
+		if (ret == -1)
+		{
+			err = errno;
+
+			if (err == EWOULDBLOCK || err == ECONNREFUSED)
+				continue;
+			Com_Printf ("NET_GetPacket: %s", NET_ErrorString());
+			continue;
+		}
+
+		if (ret == net_message->maxsize)
+		{
+			Com_Printf ("Oversize packet from %s\n", NET_AdrToString (*net_from));
+			continue;
+		}
+
+		net_message->cursize = ret;
+		SockadrToNetadr (&from, net_from);
+		return true;
+	}
+
+	return false;
+}
+
+//=============================================================================
+
+void NET_SendPacket (netsrc_t sock, int length, void *data, netadr_t to)
+{
+	int		ret;
+	struct sockaddr_in	addr;
+	int		net_socket;
+
+	if ( to.type == NA_LOOPBACK )
+	{
+		NET_SendLoopPacket (sock, length, data, to);
+		return;
+	}
+
+	if (to.type == NA_BROADCAST)
+	{
+		net_socket = ip_sockets[sock];
+		if (!net_socket)
+			return;
+	}
+	else if (to.type == NA_IP)
+	{
+		net_socket = ip_sockets[sock];
+		if (!net_socket)
+			return;
+	}
+	else if (to.type == NA_IPX)
+	{
+		net_socket = ipx_sockets[sock];
+		if (!net_socket)
+			return;
+	}
+	else if (to.type == NA_BROADCAST_IPX)
+	{
+		net_socket = ipx_sockets[sock];
+		if (!net_socket)
+			return;
+	}
+	else
+		Com_Error (ERR_FATAL, "NET_SendPacket: bad address type");
+
+	NetadrToSockadr (&to, &addr);
+
+	ret = sendto (net_socket, data, length, 0, (struct sockaddr *)&addr, sizeof(addr) );
+	if (ret == -1)
+	{
+		Com_Printf ("NET_SendPacket ERROR: %i\n", NET_ErrorString());
+	}
+}
+
+
+//=============================================================================
+
+
+
+
+/*
+====================
+NET_OpenIP
+====================
+*/
+void NET_OpenIP (void)
+{
+	cvar_t	*port, *ip;
+
+	port = Cvar_Get ("port", va("%i", PORT_SERVER), CVAR_NOSET);
+	ip = Cvar_Get ("ip", "localhost", CVAR_NOSET);
+
+	if (!ip_sockets[NS_SERVER])
+		ip_sockets[NS_SERVER] = NET_Socket (ip->string, port->value);
+	if (!ip_sockets[NS_CLIENT])
+		ip_sockets[NS_CLIENT] = NET_Socket (ip->string, PORT_ANY);
+}
+
+/*
+====================
+NET_OpenIPX
+====================
+*/
+void NET_OpenIPX (void)
+{
+}
+
+
+/*
+====================
+NET_Config
+
+A single player game will only use the loopback code
+====================
+*/
+void	NET_Config (qboolean multiplayer)
+{
+	int		i;
+
+	if (!multiplayer)
+	{	// shut down any existing sockets
+		for (i=0 ; i<2 ; i++)
+		{
+			if (ip_sockets[i])
+			{
+				close (ip_sockets[i]);
+				ip_sockets[i] = 0;
+			}
+			if (ipx_sockets[i])
+			{
+				close (ipx_sockets[i]);
+				ipx_sockets[i] = 0;
+			}
+		}
+	}
+	else
+	{	// open sockets
+		NET_OpenIP ();
+		NET_OpenIPX ();
+	}
+}
+
+
+//===================================================================
+
+
+/*
+====================
+NET_Init
+====================
+*/
+void NET_Init (void)
+{
+}
+
+
+/*
+====================
+NET_Socket
+====================
+*/
+int NET_Socket (char *net_interface, int port)
+{
+	int newsocket;
+	struct sockaddr_in address;
+	qboolean _true = true;
+	int	i = 1;
+
+	if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
+	{
+		Com_Printf ("ERROR: UDP_OpenSocket: socket:", NET_ErrorString());
+		return 0;
+	}
+
+	// make it non-blocking
+	if (ioctl (newsocket, FIONBIO, &_true) == -1)
+	{
+		Com_Printf ("ERROR: UDP_OpenSocket: ioctl FIONBIO:%s\n", NET_ErrorString());
+		return 0;
+	}
+
+	// make it broadcast capable
+	if (setsockopt(newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) == -1)
+	{
+		Com_Printf ("ERROR: UDP_OpenSocket: setsockopt SO_BROADCAST:%s\n", NET_ErrorString());
+		return 0;
+	}
+
+	if (!net_interface || !net_interface[0] || !stricmp(net_interface, "localhost"))
+		address.sin_addr.s_addr = INADDR_ANY;
+	else
+		NET_StringToSockaddr (net_interface, (struct sockaddr *)&address);
+
+	if (port == PORT_ANY)
+		address.sin_port = 0;
+	else
+		address.sin_port = htons((short)port);
+
+	address.sin_family = AF_INET;
+
+	if( bind (newsocket, (void *)&address, sizeof(address)) == -1)
+	{
+		Com_Printf ("ERROR: UDP_OpenSocket: bind: %s\n", NET_ErrorString());
+		close (newsocket);
+		return 0;
+	}
+
+	return newsocket;
+}
+
+
+/*
+====================
+NET_Shutdown
+====================
+*/
+void	NET_Shutdown (void)
+{
+	NET_Config (false);	// close sockets
+}
+
+
+/*
+====================
+NET_ErrorString
+====================
+*/
+char *NET_ErrorString (void)
+{
+	int		code;
+
+	code = errno;
+	return strerror (code);
+}
+
+// sleeps msec or until net socket is ready
+void NET_Sleep(int msec)
+{
+    struct timeval timeout;
+	fd_set	fdset;
+	extern cvar_t *dedicated;
+	extern qboolean stdin_active;
+
+	if (!ip_sockets[NS_SERVER] || (dedicated && !dedicated->value))
+		return; // we're not a server, just run full speed
+
+	FD_ZERO(&fdset);
+	if (stdin_active)
+		FD_SET(0, &fdset); // stdin is processed too
+	FD_SET(ip_sockets[NS_SERVER], &fdset); // network socket
+	timeout.tv_sec = msec/1000;
+	timeout.tv_usec = (msec%1000)*1000;
+	select(ip_sockets[NS_SERVER]+1, &fdset, NULL, NULL, &timeout);
+}
+
--- /dev/null
+++ b/plan9/q_shlinux.c
@@ -1,0 +1,205 @@
+#include <sys/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+
+#include "../linux/glob.h"
+
+#include "../qcommon/qcommon.h"
+
+//===============================================================================
+
+byte *membase;
+int maxhunksize;
+int curhunksize;
+
+void *Hunk_Begin (int maxsize)
+{
+	// reserve a huge chunk of memory, but don't commit any yet
+	maxhunksize = maxsize + sizeof(int);
+	curhunksize = 0;
+	membase = mmap(0, maxhunksize, PROT_READ|PROT_WRITE, 
+		MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+	if (membase == NULL || membase == (byte *)-1)
+		Sys_Error("unable to virtual allocate %d bytes", maxsize);
+
+	*((int *)membase) = curhunksize;
+
+	return membase + sizeof(int);
+}
+
+void *Hunk_Alloc (int size)
+{
+	byte *buf;
+
+	// round to cacheline
+	size = (size+31)&~31;
+	if (curhunksize + size > maxhunksize)
+		Sys_Error("Hunk_Alloc overflow");
+	buf = membase + sizeof(int) + curhunksize;
+	curhunksize += size;
+	return buf;
+}
+
+int Hunk_End (void)
+{
+	byte *n;
+
+	n = mremap(membase, maxhunksize, curhunksize + sizeof(int), 0);
+	if (n != membase)
+		Sys_Error("Hunk_End:  Could not remap virtual block (%d)", errno);
+	*((int *)membase) = curhunksize + sizeof(int);
+	
+	return curhunksize;
+}
+
+void Hunk_Free (void *base)
+{
+	byte *m;
+
+	if (base) {
+		m = ((byte *)base) - sizeof(int);
+		if (munmap(m, *((int *)m)))
+			Sys_Error("Hunk_Free: munmap failed (%d)", errno);
+	}
+}
+
+//===============================================================================
+
+
+/*
+================
+Sys_Milliseconds
+================
+*/
+int curtime;
+int Sys_Milliseconds (void)
+{
+	struct timeval tp;
+	struct timezone tzp;
+	static int		secbase;
+
+	gettimeofday(&tp, &tzp);
+	
+	if (!secbase)
+	{
+		secbase = tp.tv_sec;
+		return tp.tv_usec/1000;
+	}
+
+	curtime = (tp.tv_sec - secbase)*1000 + tp.tv_usec/1000;
+	
+	return curtime;
+}
+
+void Sys_Mkdir (char *path)
+{
+    mkdir (path, 0777);
+}
+
+char *strlwr (char *s)
+{
+	while (*s) {
+		*s = tolower(*s);
+		s++;
+	}
+}
+
+//============================================
+
+static	char	findbase[MAX_OSPATH];
+static	char	findpath[MAX_OSPATH];
+static	char	findpattern[MAX_OSPATH];
+static	DIR		*fdir;
+
+static qboolean CompareAttributes(char *path, char *name,
+	unsigned musthave, unsigned canthave )
+{
+	struct stat st;
+	char fn[MAX_OSPATH];
+
+// . and .. never match
+	if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
+		return false;
+
+	sprintf(fn, "%s/%s", path, name);
+	if (stat(fn, &st) == -1)
+		return false; // shouldn't happen
+
+	if ( ( st.st_mode & S_IFDIR ) && ( canthave & SFF_SUBDIR ) )
+		return false;
+
+	if ( ( musthave & SFF_SUBDIR ) && !( st.st_mode & S_IFDIR ) )
+		return false;
+
+	return true;
+}
+
+char *Sys_FindFirst (char *path, unsigned musthave, unsigned canhave)
+{
+	struct dirent *d;
+	char *p;
+
+	if (fdir)
+		Sys_Error ("Sys_BeginFind without close");
+
+//	COM_FilePath (path, findbase);
+	strcpy(findbase, path);
+
+	if ((p = strrchr(findbase, '/')) != NULL) {
+		*p = 0;
+		strcpy(findpattern, p + 1);
+	} else
+		strcpy(findpattern, "*");
+
+	if (strcmp(findpattern, "*.*") == 0)
+		strcpy(findpattern, "*");
+	
+	if ((fdir = opendir(findbase)) == NULL)
+		return NULL;
+	while ((d = readdir(fdir)) != NULL) {
+		if (!*findpattern || glob_match(findpattern, d->d_name)) {
+//			if (*findpattern)
+//				printf("%s matched %s\n", findpattern, d->d_name);
+			if (CompareAttributes(findbase, d->d_name, musthave, canhave)) {
+				sprintf (findpath, "%s/%s", findbase, d->d_name);
+				return findpath;
+			}
+		}
+	}
+	return NULL;
+}
+
+char *Sys_FindNext (unsigned musthave, unsigned canhave)
+{
+	struct dirent *d;
+
+	if (fdir == NULL)
+		return NULL;
+	while ((d = readdir(fdir)) != NULL) {
+		if (!*findpattern || glob_match(findpattern, d->d_name)) {
+//			if (*findpattern)
+//				printf("%s matched %s\n", findpattern, d->d_name);
+			if (CompareAttributes(findbase, d->d_name, musthave, canhave)) {
+				sprintf (findpath, "%s/%s", findbase, d->d_name);
+				return findpath;
+			}
+		}
+	}
+	return NULL;
+}
+
+void Sys_FindClose (void)
+{
+	if (fdir != NULL)
+		closedir(fdir);
+	fdir = NULL;
+}
+
+
+//============================================
+
--- /dev/null
+++ b/plan9/rw_linux.h
@@ -1,0 +1,16 @@
+
+
+typedef void (*Key_Event_fp_t)(int key, qboolean down);
+
+extern void (*KBD_Update_fp)(void);
+extern void (*KBD_Init_fp)(Key_Event_fp_t fp);
+extern void (*KBD_Close_fp)(void);
+
+typedef struct in_state {
+	// Pointers to functions back in client, set by vid_so
+	void (*IN_CenterView_fp)(void);
+	Key_Event_fp_t Key_Event_fp;
+	vec_t *viewangles;
+	int *in_strafe_state;
+} in_state_t;
+
--- /dev/null
+++ b/plan9/rw_x11.c
@@ -1,0 +1,1093 @@
+/*
+** RW_X11.C
+**
+** This file contains ALL Linux specific stuff having to do with the
+** software refresh.  When a port is being made the following functions
+** must be implemented by the port:
+**
+** SWimp_EndFrame
+** SWimp_Init
+** SWimp_InitGraphics
+** SWimp_SetPalette
+** SWimp_Shutdown
+** SWimp_SwitchFullscreen
+*/
+
+#include <ctype.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <X11/keysym.h>
+#include <X11/extensions/XShm.h>
+
+#include "../ref_soft/r_local.h"
+#include "../client/keys.h"
+#include "../linux/rw_linux.h"
+
+/*****************************************************************************/
+
+static qboolean			doShm;
+static Display			*x_disp;
+static Colormap			x_cmap;
+static Window			x_win;
+static GC				x_gc;
+static Visual			*x_vis;
+static XVisualInfo		*x_visinfo;
+//static XImage			*x_image;
+
+#define STD_EVENT_MASK (StructureNotifyMask | KeyPressMask \
+	     | KeyReleaseMask | ExposureMask | PointerMotionMask | \
+	     ButtonPressMask | ButtonReleaseMask)
+
+static int				x_shmeventtype;
+//static XShmSegmentInfo	x_shminfo;
+
+static qboolean			oktodraw = false;
+static qboolean			X11_active = false;
+
+int XShmQueryExtension(Display *);
+int XShmGetEventBase(Display *);
+
+int current_framebuffer;
+static XImage			*x_framebuffer[2] = { 0, 0 };
+static XShmSegmentInfo	x_shminfo[2];
+
+struct
+{
+	int key;
+	int down;
+} keyq[64];
+int keyq_head=0;
+int keyq_tail=0;
+
+int config_notify=0;
+int config_notify_width;
+int config_notify_height;
+						      
+typedef unsigned short PIXEL;
+
+// Console variables that we need to access from this module
+
+/*****************************************************************************/
+/* MOUSE                                                                     */
+/*****************************************************************************/
+
+// this is inside the renderer shared lib, so these are called from vid_so
+
+static qboolean        mouse_avail;
+static int     mouse_buttonstate;
+static int     mouse_oldbuttonstate;
+static int   mouse_x, mouse_y;
+static int	old_mouse_x, old_mouse_y;
+static int		mx, my;
+static float old_windowed_mouse;
+static int p_mouse_x, p_mouse_y;
+
+static cvar_t	*_windowed_mouse;
+static cvar_t	*m_filter;
+static cvar_t	*in_mouse;
+
+static qboolean	mlooking;
+
+// state struct passed in Init
+static in_state_t	*in_state;
+
+static cvar_t *sensitivity;
+static cvar_t *lookstrafe;
+static cvar_t *m_side;
+static cvar_t *m_yaw;
+static cvar_t *m_pitch;
+static cvar_t *m_forward;
+static cvar_t *freelook;
+
+static void Force_CenterView_f (void)
+{
+	in_state->viewangles[PITCH] = 0;
+}
+
+static void RW_IN_MLookDown (void) 
+{ 
+	mlooking = true; 
+}
+
+static void RW_IN_MLookUp (void) 
+{
+	mlooking = false;
+	in_state->IN_CenterView_fp ();
+}
+
+void RW_IN_Init(in_state_t *in_state_p)
+{
+	int mtype;
+	int i;
+
+	in_state = in_state_p;
+
+	// mouse variables
+	_windowed_mouse = ri.Cvar_Get ("_windowed_mouse", "0", CVAR_ARCHIVE);
+	m_filter = ri.Cvar_Get ("m_filter", "0", 0);
+    in_mouse = ri.Cvar_Get ("in_mouse", "1", CVAR_ARCHIVE);
+	freelook = ri.Cvar_Get( "freelook", "0", 0 );
+	lookstrafe = ri.Cvar_Get ("lookstrafe", "0", 0);
+	sensitivity = ri.Cvar_Get ("sensitivity", "3", 0);
+	m_pitch = ri.Cvar_Get ("m_pitch", "0.022", 0);
+	m_yaw = ri.Cvar_Get ("m_yaw", "0.022", 0);
+	m_forward = ri.Cvar_Get ("m_forward", "1", 0);
+	m_side = ri.Cvar_Get ("m_side", "0.8", 0);
+
+	ri.Cmd_AddCommand ("+mlook", RW_IN_MLookDown);
+	ri.Cmd_AddCommand ("-mlook", RW_IN_MLookUp);
+
+	ri.Cmd_AddCommand ("force_centerview", Force_CenterView_f);
+
+	mouse_x = mouse_y = 0.0;
+	mouse_avail = true;
+}
+
+void RW_IN_Shutdown(void)
+{
+	mouse_avail = false;
+}
+
+/*
+===========
+IN_Commands
+===========
+*/
+void RW_IN_Commands (void)
+{
+	int i;
+   
+	if (!mouse_avail) 
+		return;
+   
+	for (i=0 ; i<3 ; i++) {
+		if ( (mouse_buttonstate & (1<<i)) && !(mouse_oldbuttonstate & (1<<i)) )
+			in_state->Key_Event_fp (K_MOUSE1 + i, true);
+
+		if ( !(mouse_buttonstate & (1<<i)) && (mouse_oldbuttonstate & (1<<i)) )
+			in_state->Key_Event_fp (K_MOUSE1 + i, false);
+	}
+	mouse_oldbuttonstate = mouse_buttonstate;
+}
+
+/*
+===========
+IN_Move
+===========
+*/
+void RW_IN_Move (usercmd_t *cmd)
+{
+	if (!mouse_avail)
+		return;
+   
+	if (m_filter->value)
+	{
+		mouse_x = (mx + old_mouse_x) * 0.5;
+		mouse_y = (my + old_mouse_y) * 0.5;
+	} else {
+		mouse_x = mx;
+		mouse_y = my;
+	}
+
+	old_mouse_x = mx;
+	old_mouse_y = my;
+
+	if (!mouse_x && !mouse_y)
+		return;
+
+	mouse_x *= sensitivity->value;
+	mouse_y *= sensitivity->value;
+
+// add mouse X/Y movement to cmd
+	if ( (*in_state->in_strafe_state & 1) || 
+		(lookstrafe->value && mlooking ))
+		cmd->sidemove += m_side->value * mouse_x;
+	else
+		in_state->viewangles[YAW] -= m_yaw->value * mouse_x;
+
+	if ( (mlooking || freelook->value) && 
+		!(*in_state->in_strafe_state & 1))
+	{
+		in_state->viewangles[PITCH] += m_pitch->value * mouse_y;
+	}
+	else
+	{
+		cmd->forwardmove -= m_forward->value * mouse_y;
+	}
+	mx = my = 0;
+}
+
+void RW_IN_Frame (void)
+{
+}
+
+void RW_IN_Activate(void)
+{
+}
+
+/*****************************************************************************/
+
+static PIXEL st2d_8to16table[256];
+static int shiftmask_fl=0;
+static long r_shift,g_shift,b_shift;
+static unsigned long r_mask,g_mask,b_mask;
+
+void shiftmask_init()
+{
+    unsigned int x;
+    r_mask=x_vis->red_mask;
+    g_mask=x_vis->green_mask;
+    b_mask=x_vis->blue_mask;
+    for(r_shift=-8,x=1;x<r_mask;x=x<<1)r_shift++;
+    for(g_shift=-8,x=1;x<g_mask;x=x<<1)g_shift++;
+    for(b_shift=-8,x=1;x<b_mask;x=x<<1)b_shift++;
+    shiftmask_fl=1;
+}
+
+PIXEL xlib_rgb(int r,int g,int b)
+{
+    PIXEL p;
+    if(shiftmask_fl==0) shiftmask_init();
+    p=0;
+
+    if(r_shift>0) {
+        p=(r<<(r_shift))&r_mask;
+    } else if(r_shift<0) {
+        p=(r>>(-r_shift))&r_mask;
+    } else p|=(r&r_mask);
+
+    if(g_shift>0) {
+        p|=(g<<(g_shift))&g_mask;
+    } else if(g_shift<0) {
+        p|=(g>>(-g_shift))&g_mask;
+    } else p|=(g&g_mask);
+
+    if(b_shift>0) {
+        p|=(b<<(b_shift))&b_mask;
+    } else if(b_shift<0) {
+        p|=(b>>(-b_shift))&b_mask;
+    } else p|=(b&b_mask);
+
+    return p;
+}
+
+void st2_fixup( XImage *framebuf, int x, int y, int width, int height)
+{
+	int xi,yi;
+	unsigned char *src;
+	PIXEL *dest;
+
+	if( (x<0)||(y<0) )return;
+
+	for (yi = y; yi < (y+height); yi++) {
+		src = &framebuf->data [yi * framebuf->bytes_per_line];
+		dest = (PIXEL*)src;
+		for(xi = (x+width-1); xi >= x; xi -= 8) {
+			dest[xi  ] = st2d_8to16table[src[xi  ]];
+			dest[xi-1] = st2d_8to16table[src[xi-1]];
+			dest[xi-2] = st2d_8to16table[src[xi-2]];
+			dest[xi-3] = st2d_8to16table[src[xi-3]];
+			dest[xi-4] = st2d_8to16table[src[xi-4]];
+			dest[xi-5] = st2d_8to16table[src[xi-5]];
+			dest[xi-6] = st2d_8to16table[src[xi-6]];
+			dest[xi-7] = st2d_8to16table[src[xi-7]];
+		}
+	}
+}
+
+// ========================================================================
+// makes a null cursor
+// ========================================================================
+
+static Cursor CreateNullCursor(Display *display, Window root)
+{
+    Pixmap cursormask; 
+    XGCValues xgc;
+    GC gc;
+    XColor dummycolour;
+    Cursor cursor;
+
+    cursormask = XCreatePixmap(display, root, 1, 1, 1/*depth*/);
+    xgc.function = GXclear;
+    gc =  XCreateGC(display, cursormask, GCFunction, &xgc);
+    XFillRectangle(display, cursormask, gc, 0, 0, 1, 1);
+    dummycolour.pixel = 0;
+    dummycolour.red = 0;
+    dummycolour.flags = 04;
+    cursor = XCreatePixmapCursor(display, cursormask, cursormask,
+          &dummycolour,&dummycolour, 0,0);
+    XFreePixmap(display,cursormask);
+    XFreeGC(display,gc);
+    return cursor;
+}
+
+void ResetFrameBuffer(void)
+{
+	int mem;
+	int pwidth;
+
+	if (x_framebuffer[0])
+	{
+		free(x_framebuffer[0]->data);
+		free(x_framebuffer[0]);
+	}
+
+// alloc an extra line in case we want to wrap, and allocate the z-buffer
+	pwidth = x_visinfo->depth / 8;
+	if (pwidth == 3) pwidth = 4;
+	mem = ((vid.width*pwidth+7)&~7) * vid.height;
+
+	x_framebuffer[0] = XCreateImage(	x_disp,
+		x_vis,
+		x_visinfo->depth,
+		ZPixmap,
+		0,
+		malloc(mem),
+		vid.width, vid.height,
+		32,
+		0);
+
+	if (!x_framebuffer[0])
+		Sys_Error("VID: XCreateImage failed\n");
+
+	vid.buffer = (byte*) (x_framebuffer[0]);
+}
+
+void ResetSharedFrameBuffers(void)
+{
+	int size;
+	int key;
+	int minsize = getpagesize();
+	int frm;
+
+	for (frm=0 ; frm<2 ; frm++)
+	{
+	// free up old frame buffer memory
+		if (x_framebuffer[frm])
+		{
+			XShmDetach(x_disp, &x_shminfo[frm]);
+			free(x_framebuffer[frm]);
+			shmdt(x_shminfo[frm].shmaddr);
+		}
+
+	// create the image
+		x_framebuffer[frm] = XShmCreateImage(	x_disp,
+						x_vis,
+						x_visinfo->depth,
+						ZPixmap,
+						0,
+						&x_shminfo[frm],
+						vid.width,
+						vid.height );
+
+	// grab shared memory
+
+		size = x_framebuffer[frm]->bytes_per_line
+			* x_framebuffer[frm]->height;
+		if (size < minsize)
+			Sys_Error("VID: Window must use at least %d bytes\n", minsize);
+
+		key = random();
+		x_shminfo[frm].shmid = shmget((key_t)key, size, IPC_CREAT|0777);
+		if (x_shminfo[frm].shmid==-1)
+			Sys_Error("VID: Could not get any shared memory\n");
+
+		// attach to the shared memory segment
+		x_shminfo[frm].shmaddr =
+			(void *) shmat(x_shminfo[frm].shmid, 0, 0);
+
+		ri.Con_Printf(PRINT_ALL, 
+			"MITSHM shared memory (id=%d, addr=0x%lx)\n", 
+			x_shminfo[frm].shmid,
+			(long) x_shminfo[frm].shmaddr);
+
+		x_framebuffer[frm]->data = x_shminfo[frm].shmaddr;
+
+	// get the X server to attach to it
+
+		if (!XShmAttach(x_disp, &x_shminfo[frm]))
+			Sys_Error("VID: XShmAttach() failed\n");
+		XSync(x_disp, 0);
+		shmctl(x_shminfo[frm].shmid, IPC_RMID, 0);
+	}
+
+}
+
+// ========================================================================
+// Tragic death handler
+// ========================================================================
+
+void TragicDeath(int signal_num)
+{
+	XAutoRepeatOn(x_disp);
+	XCloseDisplay(x_disp);
+	Sys_Error("This death brought to you by the number %d\n", signal_num);
+}
+
+int XLateKey(XKeyEvent *ev)
+{
+
+	int key;
+	char buf[64];
+	KeySym keysym;
+
+	key = 0;
+
+	XLookupString(ev, buf, sizeof buf, &keysym, 0);
+
+	switch(keysym)
+	{
+		case XK_KP_Page_Up:	 key = K_KP_PGUP; break;
+		case XK_Page_Up:	 key = K_PGUP; break;
+
+		case XK_KP_Page_Down: key = K_KP_PGDN; break;
+		case XK_Page_Down:	 key = K_PGDN; break;
+
+		case XK_KP_Home: key = K_KP_HOME; break;
+		case XK_Home:	 key = K_HOME; break;
+
+		case XK_KP_End:  key = K_KP_END; break;
+		case XK_End:	 key = K_END; break;
+
+		case XK_KP_Left: key = K_KP_LEFTARROW; break;
+		case XK_Left:	 key = K_LEFTARROW; break;
+
+		case XK_KP_Right: key = K_KP_RIGHTARROW; break;
+		case XK_Right:	key = K_RIGHTARROW;		break;
+
+		case XK_KP_Down: key = K_KP_DOWNARROW; break;
+		case XK_Down:	 key = K_DOWNARROW; break;
+
+		case XK_KP_Up:   key = K_KP_UPARROW; break;
+		case XK_Up:		 key = K_UPARROW;	 break;
+
+		case XK_Escape: key = K_ESCAPE;		break;
+
+		case XK_KP_Enter: key = K_KP_ENTER;	break;
+		case XK_Return: key = K_ENTER;		 break;
+
+		case XK_Tab:		key = K_TAB;			 break;
+
+		case XK_F1:		 key = K_F1;				break;
+
+		case XK_F2:		 key = K_F2;				break;
+
+		case XK_F3:		 key = K_F3;				break;
+
+		case XK_F4:		 key = K_F4;				break;
+
+		case XK_F5:		 key = K_F5;				break;
+
+		case XK_F6:		 key = K_F6;				break;
+
+		case XK_F7:		 key = K_F7;				break;
+
+		case XK_F8:		 key = K_F8;				break;
+
+		case XK_F9:		 key = K_F9;				break;
+
+		case XK_F10:		key = K_F10;			 break;
+
+		case XK_F11:		key = K_F11;			 break;
+
+		case XK_F12:		key = K_F12;			 break;
+
+		case XK_BackSpace: key = K_BACKSPACE; break;
+
+		case XK_KP_Delete: key = K_KP_DEL; break;
+		case XK_Delete: key = K_DEL; break;
+
+		case XK_Pause:	key = K_PAUSE;		 break;
+
+		case XK_Shift_L:
+		case XK_Shift_R:	key = K_SHIFT;		break;
+
+		case XK_Execute: 
+		case XK_Control_L: 
+		case XK_Control_R:	key = K_CTRL;		 break;
+
+		case XK_Alt_L:	
+		case XK_Meta_L: 
+		case XK_Alt_R:	
+		case XK_Meta_R: key = K_ALT;			break;
+
+		case XK_KP_Begin: key = K_KP_5;	break;
+
+		case XK_Insert:key = K_INS; break;
+		case XK_KP_Insert: key = K_KP_INS; break;
+
+		case XK_KP_Multiply: key = '*'; break;
+		case XK_KP_Add:  key = K_KP_PLUS; break;
+		case XK_KP_Subtract: key = K_KP_MINUS; break;
+		case XK_KP_Divide: key = K_KP_SLASH; break;
+
+#if 0
+		case 0x021: key = '1';break;/* [!] */
+		case 0x040: key = '2';break;/* [@] */
+		case 0x023: key = '3';break;/* [#] */
+		case 0x024: key = '4';break;/* [$] */
+		case 0x025: key = '5';break;/* [%] */
+		case 0x05e: key = '6';break;/* [^] */
+		case 0x026: key = '7';break;/* [&] */
+		case 0x02a: key = '8';break;/* [*] */
+		case 0x028: key = '9';;break;/* [(] */
+		case 0x029: key = '0';break;/* [)] */
+		case 0x05f: key = '-';break;/* [_] */
+		case 0x02b: key = '=';break;/* [+] */
+		case 0x07c: key = '\'';break;/* [|] */
+		case 0x07d: key = '[';break;/* [}] */
+		case 0x07b: key = ']';break;/* [{] */
+		case 0x022: key = '\'';break;/* ["] */
+		case 0x03a: key = ';';break;/* [:] */
+		case 0x03f: key = '/';break;/* [?] */
+		case 0x03e: key = '.';break;/* [>] */
+		case 0x03c: key = ',';break;/* [<] */
+#endif
+
+		default:
+			key = *(unsigned char*)buf;
+			if (key >= 'A' && key <= 'Z')
+				key = key - 'A' + 'a';
+			break;
+	} 
+
+	return key;
+}
+
+void GetEvent(void)
+{
+	XEvent x_event;
+	int b;
+   
+	XNextEvent(x_disp, &x_event);
+	switch(x_event.type) {
+	case KeyPress:
+		keyq[keyq_head].key = XLateKey(&x_event.xkey);
+		keyq[keyq_head].down = true;
+		keyq_head = (keyq_head + 1) & 63;
+		break;
+	case KeyRelease:
+		keyq[keyq_head].key = XLateKey(&x_event.xkey);
+		keyq[keyq_head].down = false;
+		keyq_head = (keyq_head + 1) & 63;
+		break;
+
+	case MotionNotify:
+		if (_windowed_mouse->value) {
+			mx += ((int)x_event.xmotion.x - (int)(vid.width/2));
+			my += ((int)x_event.xmotion.y - (int)(vid.height/2));
+
+			/* move the mouse to the window center again */
+			XSelectInput(x_disp,x_win, STD_EVENT_MASK & ~PointerMotionMask);
+			XWarpPointer(x_disp,None,x_win,0,0,0,0, 
+				(vid.width/2),(vid.height/2));
+			XSelectInput(x_disp,x_win, STD_EVENT_MASK);
+		} else {
+			mx = ((int)x_event.xmotion.x - (int)p_mouse_x);
+			my = ((int)x_event.xmotion.y - (int)p_mouse_y);
+			p_mouse_x=x_event.xmotion.x;
+			p_mouse_y=x_event.xmotion.y;
+		}
+		break;
+
+	case ButtonPress:
+		b=-1;
+		if (x_event.xbutton.button == 1)
+			b = 0;
+		else if (x_event.xbutton.button == 2)
+			b = 2;
+		else if (x_event.xbutton.button == 3)
+			b = 1;
+		if (b>=0)
+			mouse_buttonstate |= 1<<b;
+		break;
+
+	case ButtonRelease:
+		b=-1;
+		if (x_event.xbutton.button == 1)
+			b = 0;
+		else if (x_event.xbutton.button == 2)
+			b = 2;
+		else if (x_event.xbutton.button == 3)
+			b = 1;
+		if (b>=0)
+			mouse_buttonstate &= ~(1<<b);
+		break;
+	
+	case ConfigureNotify:
+		config_notify_width = x_event.xconfigure.width;
+		config_notify_height = x_event.xconfigure.height;
+		config_notify = 1;
+		break;
+
+	default:
+		if (doShm && x_event.type == x_shmeventtype)
+			oktodraw = true;
+	}
+   
+	if (old_windowed_mouse != _windowed_mouse->value) {
+		old_windowed_mouse = _windowed_mouse->value;
+
+		if (!_windowed_mouse->value) {
+			/* ungrab the pointer */
+			XUngrabPointer(x_disp,CurrentTime);
+		} else {
+			/* grab the pointer */
+			XGrabPointer(x_disp,x_win,True,0,GrabModeAsync,
+				GrabModeAsync,x_win,None,CurrentTime);
+		}
+	}
+}
+
+/*****************************************************************************/
+
+/*
+** SWimp_Init
+**
+** This routine is responsible for initializing the implementation
+** specific stuff in a software rendering subsystem.
+*/
+int SWimp_Init( void *hInstance, void *wndProc )
+{
+// open the display
+	x_disp = XOpenDisplay(0);
+	if (!x_disp)
+	{
+		if (getenv("DISPLAY"))
+			Sys_Error("VID: Could not open display [%s]\n",
+				getenv("DISPLAY"));
+		else
+			Sys_Error("VID: Could not open local display\n");
+	}
+
+// catch signals so i can turn on auto-repeat
+
+	{
+		struct sigaction sa;
+		sigaction(SIGINT, 0, &sa);
+		sa.sa_handler = TragicDeath;
+		sigaction(SIGINT, &sa, 0);
+		sigaction(SIGTERM, &sa, 0);
+	}
+
+	return true;
+}
+
+/*
+** SWimp_InitGraphics
+**
+** This initializes the software refresh's implementation specific
+** graphics subsystem.  In the case of Windows it creates DIB or
+** DDRAW surfaces.
+**
+** The necessary width and height parameters are grabbed from
+** vid.width and vid.height.
+*/
+static qboolean SWimp_InitGraphics( qboolean fullscreen )
+{
+	int pnum, i;
+	XVisualInfo template;
+	int num_visuals;
+	int template_mask;
+
+	srandom(getpid());
+
+	// free resources in use
+	SWimp_Shutdown ();
+
+	// let the sound and input subsystems know about the new window
+	ri.Vid_NewWindow (vid.width, vid.height);
+
+	XAutoRepeatOff(x_disp);
+
+// for debugging only
+	XSynchronize(x_disp, True);
+
+// check for command-line window size
+	template_mask = 0;
+
+#if 0
+// specify a visual id
+	if ((pnum=COM_CheckParm("-visualid")))
+	{
+		if (pnum >= com_argc-1)
+			Sys_Error("VID: -visualid <id#>\n");
+		template.visualid = Q_atoi(com_argv[pnum+1]);
+		template_mask = VisualIDMask;
+	}
+
+// If not specified, use default visual
+	else
+#endif
+	{
+		int screen;
+		screen = XDefaultScreen(x_disp);
+		template.visualid =
+			XVisualIDFromVisual(XDefaultVisual(x_disp, screen));
+		template_mask = VisualIDMask;
+	}
+
+// pick a visual- warn if more than one was available
+	x_visinfo = XGetVisualInfo(x_disp, template_mask, &template, &num_visuals);
+	if (num_visuals > 1)
+	{
+		printf("Found more than one visual id at depth %d:\n", template.depth);
+		for (i=0 ; i<num_visuals ; i++)
+			printf("	-visualid %d\n", (int)(x_visinfo[i].visualid));
+	}
+	else if (num_visuals == 0)
+	{
+		if (template_mask == VisualIDMask)
+			Sys_Error("VID: Bad visual id %d\n", template.visualid);
+		else
+			Sys_Error("VID: No visuals at depth %d\n", template.depth);
+	}
+
+#if 0
+	if (verbose)
+	{
+		printf("Using visualid %d:\n", (int)(x_visinfo->visualid));
+		printf("	screen %d\n", x_visinfo->screen);
+		printf("	red_mask 0x%x\n", (int)(x_visinfo->red_mask));
+		printf("	green_mask 0x%x\n", (int)(x_visinfo->green_mask));
+		printf("	blue_mask 0x%x\n", (int)(x_visinfo->blue_mask));
+		printf("	colormap_size %d\n", x_visinfo->colormap_size);
+		printf("	bits_per_rgb %d\n", x_visinfo->bits_per_rgb);
+	}
+#endif
+
+	x_vis = x_visinfo->visual;
+
+// setup attributes for main window
+	{
+	   int attribmask = CWEventMask  | CWColormap | CWBorderPixel;
+	   XSetWindowAttributes attribs;
+	   Colormap tmpcmap;
+	   
+	   tmpcmap = XCreateColormap(x_disp, XRootWindow(x_disp,
+							 x_visinfo->screen), x_vis, AllocNone);
+	   
+	   attribs.event_mask = STD_EVENT_MASK;
+	   attribs.border_pixel = 0;
+	   attribs.colormap = tmpcmap;
+
+// create the main window
+		x_win = XCreateWindow(	x_disp,
+			XRootWindow(x_disp, x_visinfo->screen),
+			0, 0,	// x, y
+			vid.width, vid.height,
+			0, // borderwidth
+			x_visinfo->depth,
+			InputOutput,
+			x_vis,
+			attribmask,
+			&attribs );
+		XStoreName(x_disp, x_win, "Quake II");
+
+		if (x_visinfo->class != TrueColor)
+			XFreeColormap(x_disp, tmpcmap);
+	}
+
+	if (x_visinfo->depth == 8)
+	{
+	// create and upload the palette
+		if (x_visinfo->class == PseudoColor)
+		{
+			x_cmap = XCreateColormap(x_disp, x_win, x_vis, AllocAll);
+			XSetWindowColormap(x_disp, x_win, x_cmap);
+		}
+
+	}
+
+// inviso cursor
+	XDefineCursor(x_disp, x_win, CreateNullCursor(x_disp, x_win));
+
+// create the GC
+	{
+		XGCValues xgcvalues;
+		int valuemask = GCGraphicsExposures;
+		xgcvalues.graphics_exposures = False;
+		x_gc = XCreateGC(x_disp, x_win, valuemask, &xgcvalues );
+	}
+
+// map the window
+	XMapWindow(x_disp, x_win);
+
+// wait for first exposure event
+	{
+		XEvent event;
+		do
+		{
+			XNextEvent(x_disp, &event);
+			if (event.type == Expose && !event.xexpose.count)
+				oktodraw = true;
+		} while (!oktodraw);
+	}
+// now safe to draw
+
+// even if MITSHM is available, make sure it's a local connection
+	if (XShmQueryExtension(x_disp))
+	{
+		char *displayname;
+		doShm = true;
+		displayname = (char *) getenv("DISPLAY");
+		if (displayname)
+		{
+			char *d = displayname;
+			while (*d && (*d != ':')) d++;
+			if (*d) *d = 0;
+			if (!(!strcasecmp(displayname, "unix") || !*displayname))
+				doShm = false;
+		}
+	}
+
+	if (doShm)
+	{
+		x_shmeventtype = XShmGetEventBase(x_disp) + ShmCompletion;
+		ResetSharedFrameBuffers();
+	}
+	else
+		ResetFrameBuffer();
+
+	current_framebuffer = 0;
+	vid.rowbytes = x_framebuffer[0]->bytes_per_line;
+	vid.buffer = x_framebuffer[0]->data;
+
+//	XSynchronize(x_disp, False);
+
+	X11_active = true;
+
+	return true;
+}
+
+/*
+** SWimp_EndFrame
+**
+** This does an implementation specific copy from the backbuffer to the
+** front buffer.  In the Win32 case it uses BitBlt or BltFast depending
+** on whether we're using DIB sections/GDI or DDRAW.
+*/
+void SWimp_EndFrame (void)
+{
+// if the window changes dimension, skip this frame
+#if 0
+	if (config_notify)
+	{
+		fprintf(stderr, "config notify\n");
+		config_notify = 0;
+		vid.width = config_notify_width & ~7;
+		vid.height = config_notify_height;
+		if (doShm)
+			ResetSharedFrameBuffers();
+		else
+			ResetFrameBuffer();
+		vid.rowbytes = x_framebuffer[0]->bytes_per_line;
+		vid.buffer = x_framebuffer[current_framebuffer]->data;
+		vid.recalc_refdef = 1;				// force a surface cache flush
+		Con_CheckResize();
+		Con_Clear_f();
+		return;
+	}
+#endif
+
+	if (doShm)
+	{
+
+		if (x_visinfo->depth != 8)
+			st2_fixup( x_framebuffer[current_framebuffer], 
+				0, 0, vid.width, vid.height);	
+		if (!XShmPutImage(x_disp, x_win, x_gc,
+			x_framebuffer[current_framebuffer], 0, 0,
+			0, 0, vid.width, vid.height, True))
+				Sys_Error("VID_Update: XShmPutImage failed\n");
+		oktodraw = false;
+		while (!oktodraw) 
+			GetEvent();
+		current_framebuffer = !current_framebuffer;
+		vid.buffer = x_framebuffer[current_framebuffer]->data;
+		XSync(x_disp, False);
+	}
+	else
+	{
+		if (x_visinfo->depth != 8)
+			st2_fixup( x_framebuffer[current_framebuffer], 
+				0, 0, vid.width, vid.height);
+		XPutImage(x_disp, x_win, x_gc, x_framebuffer[0],
+			0, 0, 0, 0, vid.width, vid.height);
+		XSync(x_disp, False);
+	}
+}
+
+/*
+** SWimp_SetMode
+*/
+rserr_t SWimp_SetMode( int *pwidth, int *pheight, int mode, qboolean fullscreen )
+{
+	rserr_t retval = rserr_ok;
+
+	ri.Con_Printf (PRINT_ALL, "setting mode %d:", mode );
+
+	if ( !ri.Vid_GetModeInfo( pwidth, pheight, mode ) )
+	{
+		ri.Con_Printf( PRINT_ALL, " invalid mode\n" );
+		return rserr_invalid_mode;
+	}
+
+	ri.Con_Printf( PRINT_ALL, " %d %d\n", *pwidth, *pheight);
+
+	if ( !SWimp_InitGraphics( false ) ) {
+		// failed to set a valid mode in windowed mode
+		return rserr_invalid_mode;
+	}
+
+	R_GammaCorrectAndSetPalette( ( const unsigned char * ) d_8to24table );
+
+	return retval;
+}
+
+/*
+** SWimp_SetPalette
+**
+** System specific palette setting routine.  A NULL palette means
+** to use the existing palette.  The palette is expected to be in
+** a padded 4-byte xRGB format.
+*/
+void SWimp_SetPalette( const unsigned char *palette )
+{
+	int i;
+	XColor colors[256];
+
+	if (!X11_active)
+		return;
+
+    if ( !palette )
+        palette = ( const unsigned char * ) sw_state.currentpalette;
+ 
+	for(i=0;i<256;i++)
+		st2d_8to16table[i]= xlib_rgb(palette[i*4],
+			palette[i*4+1],palette[i*4+2]);
+
+	if (x_visinfo->class == PseudoColor && x_visinfo->depth == 8)
+	{
+		for (i=0 ; i<256 ; i++)
+		{
+			colors[i].pixel = i;
+			colors[i].flags = DoRed|DoGreen|DoBlue;
+			colors[i].red = palette[i*4] * 257;
+			colors[i].green = palette[i*4+1] * 257;
+			colors[i].blue = palette[i*4+2] * 257;
+		}
+		XStoreColors(x_disp, x_cmap, colors, 256);
+	}
+}
+
+/*
+** SWimp_Shutdown
+**
+** System specific graphics subsystem shutdown routine.  Destroys
+** DIBs or DDRAW surfaces as appropriate.
+*/
+void SWimp_Shutdown( void )
+{
+	int i;
+
+	if (!X11_active)
+		return;
+
+	if (doShm) {
+		for (i = 0; i < 2; i++)
+			if (x_framebuffer[i]) {
+				XShmDetach(x_disp, &x_shminfo[i]);
+				free(x_framebuffer[i]);
+				shmdt(x_shminfo[i].shmaddr);
+				x_framebuffer[i] = NULL;
+			}
+	} else if (x_framebuffer[0]) {
+		free(x_framebuffer[0]->data);
+		free(x_framebuffer[0]);
+		x_framebuffer[0] = NULL;
+	}
+
+	XDestroyWindow(	x_disp, x_win );
+
+	XAutoRepeatOn(x_disp);
+//	XCloseDisplay(x_disp);
+
+	X11_active = false;
+}
+
+/*
+** SWimp_AppActivate
+*/
+void SWimp_AppActivate( qboolean active )
+{
+}
+
+//===============================================================================
+
+/*
+================
+Sys_MakeCodeWriteable
+================
+*/
+void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length)
+{
+
+	int r;
+	unsigned long addr;
+	int psize = getpagesize();
+
+	addr = (startaddr & ~(psize-1)) - psize;
+
+//	fprintf(stderr, "writable code %lx(%lx)-%lx, length=%lx\n", startaddr,
+//			addr, startaddr+length, length);
+
+	r = mprotect((char*)addr, length + startaddr - addr + psize, 7);
+
+	if (r < 0)
+    		Sys_Error("Protection change failed\n");
+
+}
+
+/*****************************************************************************/
+/* KEYBOARD                                                                  */
+/*****************************************************************************/
+
+Key_Event_fp_t Key_Event_fp;
+
+void KBD_Init(Key_Event_fp_t fp)
+{
+	Key_Event_fp = fp;
+}
+
+void KBD_Update(void)
+{
+// get events from x server
+	if (x_disp)
+	{
+		while (XPending(x_disp)) 
+			GetEvent();
+		while (keyq_head != keyq_tail)
+		{
+			Key_Event_fp(keyq[keyq_tail].key, keyq[keyq_tail].down);
+			keyq_tail = (keyq_tail + 1) & 63;
+		}
+	}
+}
+
+void KBD_Close(void)
+{
+}
+
+
--- /dev/null
+++ b/plan9/snd_linux.c
@@ -1,0 +1,266 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/shm.h>
+#include <sys/wait.h>
+#include <linux/soundcard.h>
+#include <stdio.h>
+
+#include "../client/client.h"
+#include "../client/snd_loc.h"
+
+int audio_fd;
+int snd_inited;
+
+cvar_t *sndbits;
+cvar_t *sndspeed;
+cvar_t *sndchannels;
+cvar_t *snddevice;
+
+static int tryrates[] = { 11025, 22051, 44100, 8000 };
+
+qboolean SNDDMA_Init(void)
+{
+
+	int rc;
+    int fmt;
+	int tmp;
+    int i;
+    char *s;
+	struct audio_buf_info info;
+	int caps;
+	extern uid_t saved_euid;
+
+	if (snd_inited)
+		return;
+
+	if (!snddevice) {
+		sndbits = Cvar_Get("sndbits", "16", CVAR_ARCHIVE);
+		sndspeed = Cvar_Get("sndspeed", "0", CVAR_ARCHIVE);
+		sndchannels = Cvar_Get("sndchannels", "2", CVAR_ARCHIVE);
+		snddevice = Cvar_Get("snddevice", "/dev/dsp", CVAR_ARCHIVE);
+	}
+
+// open /dev/dsp, confirm capability to mmap, and get size of dma buffer
+
+	if (!audio_fd) {
+		seteuid(saved_euid);
+
+		audio_fd = open(snddevice->string, O_RDWR);
+
+		seteuid(getuid());
+
+		if (audio_fd < 0)
+		{
+			perror(snddevice->string);
+			Com_Printf("Could not open %s\n", snddevice->string);
+			return 0;
+		}
+	}
+
+    rc = ioctl(audio_fd, SNDCTL_DSP_RESET, 0);
+    if (rc < 0)
+	{
+		perror(snddevice->string);
+		Com_Printf("Could not reset %s\n", snddevice->string);
+		close(audio_fd);
+		return 0;
+	}
+
+	if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps)==-1)
+	{
+		perror(snddevice->string);
+        Com_Printf("Sound driver too old\n");
+		close(audio_fd);
+		return 0;
+	}
+
+	if (!(caps & DSP_CAP_TRIGGER) || !(caps & DSP_CAP_MMAP))
+	{
+		Com_Printf("Sorry but your soundcard can't do this\n");
+		close(audio_fd);
+		return 0;
+	}
+
+    if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info)==-1)
+    {   
+        perror("GETOSPACE");
+		Com_Printf("Um, can't do GETOSPACE?\n");
+		close(audio_fd);
+		return 0;
+    }
+    
+// set sample bits & speed
+
+    dma.samplebits = (int)sndbits->value;
+	if (dma.samplebits != 16 && dma.samplebits != 8)
+    {
+        ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &fmt);
+        if (fmt & AFMT_S16_LE) dma.samplebits = 16;
+        else if (fmt & AFMT_U8) dma.samplebits = 8;
+    }
+
+	dma.speed = (int)sndspeed->value;
+	if (!dma.speed) {
+        for (i=0 ; i<sizeof(tryrates)/4 ; i++)
+            if (!ioctl(audio_fd, SNDCTL_DSP_SPEED, &tryrates[i])) break;
+        dma.speed = tryrates[i];
+    }
+
+	dma.channels = (int)sndchannels->value;
+	if (dma.channels < 1 || dma.channels > 2)
+		dma.channels = 2;
+	
+	dma.samples = info.fragstotal * info.fragsize / (dma.samplebits/8);
+	dma.submission_chunk = 1;
+
+// memory map the dma buffer
+
+	if (!dma.buffer)
+		dma.buffer = (unsigned char *) mmap(NULL, info.fragstotal
+			* info.fragsize, PROT_WRITE, MAP_FILE|MAP_SHARED, audio_fd, 0);
+	if (!dma.buffer)
+	{
+		perror(snddevice->string);
+		Com_Printf("Could not mmap %s\n", snddevice->string);
+		close(audio_fd);
+		return 0;
+	}
+
+	tmp = 0;
+	if (dma.channels == 2)
+		tmp = 1;
+    rc = ioctl(audio_fd, SNDCTL_DSP_STEREO, &tmp);
+    if (rc < 0)
+    {
+		perror(snddevice->string);
+        Com_Printf("Could not set %s to stereo=%d", snddevice->string, dma.channels);
+		close(audio_fd);
+        return 0;
+    }
+	if (tmp)
+		dma.channels = 2;
+	else
+		dma.channels = 1;
+
+    rc = ioctl(audio_fd, SNDCTL_DSP_SPEED, &dma.speed);
+    if (rc < 0)
+    {
+		perror(snddevice->string);
+        Com_Printf("Could not set %s speed to %d", snddevice->string, dma.speed);
+		close(audio_fd);
+        return 0;
+    }
+
+    if (dma.samplebits == 16)
+    {
+        rc = AFMT_S16_LE;
+        rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc);
+        if (rc < 0)
+		{
+			perror(snddevice->string);
+			Com_Printf("Could not support 16-bit data.  Try 8-bit.\n");
+			close(audio_fd);
+			return 0;
+		}
+    }
+    else if (dma.samplebits == 8)
+    {
+        rc = AFMT_U8;
+        rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc);
+        if (rc < 0)
+		{
+			perror(snddevice->string);
+			Com_Printf("Could not support 8-bit data.\n");
+			close(audio_fd);
+			return 0;
+		}
+    }
+	else
+	{
+		perror(snddevice->string);
+		Com_Printf("%d-bit sound not supported.", dma.samplebits);
+		close(audio_fd);
+		return 0;
+	}
+
+// toggle the trigger & start her up
+
+    tmp = 0;
+    rc  = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp);
+	if (rc < 0)
+	{
+		perror(snddevice->string);
+		Com_Printf("Could not toggle.\n");
+		close(audio_fd);
+		return 0;
+	}
+    tmp = PCM_ENABLE_OUTPUT;
+    rc = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp);
+	if (rc < 0)
+	{
+		perror(snddevice->string);
+		Com_Printf("Could not toggle.\n");
+		close(audio_fd);
+		return 0;
+	}
+
+	dma.samplepos = 0;
+
+	snd_inited = 1;
+	return 1;
+
+}
+
+int SNDDMA_GetDMAPos(void)
+{
+
+	struct count_info count;
+
+	if (!snd_inited) return 0;
+
+	if (ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &count)==-1)
+	{
+		perror(snddevice->string);
+		Com_Printf("Uh, sound dead.\n");
+		close(audio_fd);
+		snd_inited = 0;
+		return 0;
+	}
+//	dma.samplepos = (count.bytes / (dma.samplebits / 8)) & (dma.samples-1);
+//	fprintf(stderr, "%d    \r", count.ptr);
+	dma.samplepos = count.ptr / (dma.samplebits / 8);
+
+	return dma.samplepos;
+
+}
+
+void SNDDMA_Shutdown(void)
+{
+#if 0
+	if (snd_inited)
+	{
+		close(audio_fd);
+		snd_inited = 0;
+	}
+#endif
+}
+
+/*
+==============
+SNDDMA_Submit
+
+Send sound to device if buffer isn't really the dma buffer
+===============
+*/
+void SNDDMA_Submit(void)
+{
+}
+
+void SNDDMA_BeginPainting (void)
+{
+}
+
--- /dev/null
+++ b/plan9/sys_linux.c
@@ -1,0 +1,381 @@
+#include <unistd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <mntent.h>
+
+#include <dlfcn.h>
+
+#include "../qcommon/qcommon.h"
+
+#include "../linux/rw_linux.h"
+
+cvar_t *nostdout;
+
+unsigned	sys_frame_time;
+
+uid_t saved_euid;
+qboolean stdin_active = true;
+
+// =======================================================================
+// General routines
+// =======================================================================
+
+void Sys_ConsoleOutput (char *string)
+{
+	if (nostdout && nostdout->value)
+		return;
+
+	fputs(string, stdout);
+}
+
+void Sys_Printf (char *fmt, ...)
+{
+	va_list		argptr;
+	char		text[1024];
+	unsigned char		*p;
+
+	va_start (argptr,fmt);
+	vsprintf (text,fmt,argptr);
+	va_end (argptr);
+
+	if (strlen(text) > sizeof(text))
+		Sys_Error("memory overwrite in Sys_Printf");
+
+    if (nostdout && nostdout->value)
+        return;
+
+	for (p = (unsigned char *)text; *p; p++) {
+		*p &= 0x7f;
+		if ((*p > 128 || *p < 32) && *p != 10 && *p != 13 && *p != 9)
+			printf("[%02x]", *p);
+		else
+			putc(*p, stdout);
+	}
+}
+
+void Sys_Quit (void)
+{
+	CL_Shutdown ();
+	Qcommon_Shutdown ();
+    fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY);
+	_exit(0);
+}
+
+void Sys_Init(void)
+{
+#if id386
+//	Sys_SetFPCW();
+#endif
+}
+
+void Sys_Error (char *error, ...)
+{ 
+    va_list     argptr;
+    char        string[1024];
+
+// change stdin to non blocking
+    fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY);
+
+	CL_Shutdown ();
+	Qcommon_Shutdown ();
+    
+    va_start (argptr,error);
+    vsprintf (string,error,argptr);
+    va_end (argptr);
+	fprintf(stderr, "Error: %s\n", string);
+
+	_exit (1);
+
+} 
+
+void Sys_Warn (char *warning, ...)
+{ 
+    va_list     argptr;
+    char        string[1024];
+    
+    va_start (argptr,warning);
+    vsprintf (string,warning,argptr);
+    va_end (argptr);
+	fprintf(stderr, "Warning: %s", string);
+} 
+
+/*
+============
+Sys_FileTime
+
+returns -1 if not present
+============
+*/
+int	Sys_FileTime (char *path)
+{
+	struct	stat	buf;
+	
+	if (stat (path,&buf) == -1)
+		return -1;
+	
+	return buf.st_mtime;
+}
+
+void floating_point_exception_handler(int whatever)
+{
+//	Sys_Warn("floating point exception\n");
+	signal(SIGFPE, floating_point_exception_handler);
+}
+
+char *Sys_ConsoleInput(void)
+{
+    static char text[256];
+    int     len;
+	fd_set	fdset;
+    struct timeval timeout;
+
+	if (!dedicated || !dedicated->value)
+		return NULL;
+
+	if (!stdin_active)
+		return NULL;
+
+	FD_ZERO(&fdset);
+	FD_SET(0, &fdset); // stdin
+	timeout.tv_sec = 0;
+	timeout.tv_usec = 0;
+	if (select (1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(0, &fdset))
+		return NULL;
+
+	len = read (0, text, sizeof(text));
+	if (len == 0) { // eof!
+		stdin_active = false;
+		return NULL;
+	}
+
+	if (len < 1)
+		return NULL;
+	text[len-1] = 0;    // rip off the /n and terminate
+
+	return text;
+}
+
+/*****************************************************************************/
+
+static void *game_library;
+
+/*
+=================
+Sys_UnloadGame
+=================
+*/
+void Sys_UnloadGame (void)
+{
+	if (game_library) 
+		dlclose (game_library);
+	game_library = NULL;
+}
+
+/*
+=================
+Sys_GetGameAPI
+
+Loads the game dll
+=================
+*/
+void *Sys_GetGameAPI (void *parms)
+{
+	void	*(*GetGameAPI) (void *);
+
+	char	name[MAX_OSPATH];
+	char	curpath[MAX_OSPATH];
+	char	*path;
+#ifdef __i386__
+	const char *gamename = "gamei386.so";
+#elif defined __alpha__
+	const char *gamename = "gameaxp.so";
+#else
+#error Unknown arch
+#endif
+
+	setreuid(getuid(), getuid());
+	setegid(getgid());
+
+	if (game_library)
+		Com_Error (ERR_FATAL, "Sys_GetGameAPI without Sys_UnloadingGame");
+
+	getcwd(curpath, sizeof(curpath));
+
+	Com_Printf("------- Loading %s -------", gamename);
+
+	// now run through the search paths
+	path = NULL;
+	while (1)
+	{
+		path = FS_NextPath (path);
+		if (!path)
+			return NULL;		// couldn't find one anywhere
+		sprintf (name, "%s/%s/%s", curpath, path, gamename);
+		game_library = dlopen (name, RTLD_NOW );
+		if (game_library)
+		{
+			Com_DPrintf ("LoadLibrary (%s)\n",name);
+			break;
+		}
+		else
+			printf("dlerror: %s\n", dlerror());
+	}
+
+	GetGameAPI = (void *)dlsym (game_library, "GetGameAPI");
+	if (!GetGameAPI)
+	{
+		Sys_UnloadGame ();		
+		return NULL;
+	}
+
+	return GetGameAPI (parms);
+}
+
+/*****************************************************************************/
+
+void Sys_AppActivate (void)
+{
+}
+
+void Sys_SendKeyEvents (void)
+{
+#ifndef DEDICATED_ONLY
+	if (KBD_Update_fp)
+		KBD_Update_fp();
+#endif
+
+	// grab frame time 
+	sys_frame_time = Sys_Milliseconds();
+}
+
+/*****************************************************************************/
+
+char *Sys_GetClipboardData(void)
+{
+	return NULL;
+}
+
+int main (int argc, char **argv)
+{
+	int 	time, oldtime, newtime;
+
+	// go back to real user for config loads
+	saved_euid = geteuid();
+	seteuid(getuid());
+
+	Qcommon_Init(argc, argv);
+
+	fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY);
+
+	nostdout = Cvar_Get("nostdout", "0", 0);
+	if (!nostdout->value) {
+		fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY);
+//		printf ("Linux Quake -- Version %0.3f\n", LINUX_VERSION);
+	}
+
+    oldtime = Sys_Milliseconds ();
+    while (1)
+    {
+// find time spent rendering last frame
+		do {
+			newtime = Sys_Milliseconds ();
+			time = newtime - oldtime;
+		} while (time < 1);
+        Qcommon_Frame (time);
+		oldtime = newtime;
+    }
+
+}
+
+void Sys_CopyProtect(void)
+{
+	FILE *mnt;
+	struct mntent *ent;
+	char path[MAX_OSPATH];
+	struct stat st;
+	qboolean found_cd = false;
+
+	static qboolean checked = false;
+
+	if (checked)
+		return;
+
+	if ((mnt = setmntent("/etc/mtab", "r")) == NULL)
+		Com_Error(ERR_FATAL, "Can't read mount table to determine mounted cd location.");
+
+	while ((ent = getmntent(mnt)) != NULL) {
+		if (strcmp(ent->mnt_type, "iso9660") == 0) {
+			// found a cd file system
+			found_cd = true;
+			sprintf(path, "%s/%s", ent->mnt_dir, "install/data/quake2.exe");
+			if (stat(path, &st) == 0) {
+				// found it
+				checked = true;
+				endmntent(mnt);
+				return;
+			}
+			sprintf(path, "%s/%s", ent->mnt_dir, "Install/Data/quake2.exe");
+			if (stat(path, &st) == 0) {
+				// found it
+				checked = true;
+				endmntent(mnt);
+				return;
+			}
+			sprintf(path, "%s/%s", ent->mnt_dir, "quake2.exe");
+			if (stat(path, &st) == 0) {
+				// found it
+				checked = true;
+				endmntent(mnt);
+				return;
+			}
+		}
+	}
+	endmntent(mnt);
+
+	if (found_cd)
+		Com_Error (ERR_FATAL, "Could not find a Quake2 CD in your CD drive.");
+	Com_Error (ERR_FATAL, "Unable to find a mounted iso9660 file system.\n"
+		"You must mount the Quake2 CD in a cdrom drive in order to play.");
+}
+
+#if 0
+/*
+================
+Sys_MakeCodeWriteable
+================
+*/
+void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length)
+{
+
+	int r;
+	unsigned long addr;
+	int psize = getpagesize();
+
+	addr = (startaddr & ~(psize-1)) - psize;
+
+//	fprintf(stderr, "writable code %lx(%lx)-%lx, length=%lx\n", startaddr,
+//			addr, startaddr+length, length);
+
+	r = mprotect((char*)addr, length + startaddr - addr + psize, 7);
+
+	if (r < 0)
+    		Sys_Error("Protection change failed\n");
+
+}
+
+#endif
--- /dev/null
+++ b/plan9/vid_menu.c
@@ -1,0 +1,437 @@
+#include "../client/client.h"
+#include "../client/qmenu.h"
+
+#define REF_SOFT	0
+#define REF_SOFTX11	1
+#define REF_OPENGL	2
+
+extern cvar_t *vid_ref;
+extern cvar_t *vid_fullscreen;
+extern cvar_t *vid_gamma;
+extern cvar_t *scr_viewsize;
+
+static cvar_t *gl_mode;
+static cvar_t *gl_driver;
+static cvar_t *gl_picmip;
+static cvar_t *gl_ext_palettedtexture;
+
+static cvar_t *sw_mode;
+static cvar_t *sw_stipplealpha;
+
+static cvar_t *_windowed_mouse;
+
+extern void M_ForceMenuOff( void );
+
+/*
+====================================================================
+
+MENU INTERACTION
+
+====================================================================
+*/
+#define SOFTWARE_MENU 0
+#define OPENGL_MENU   1
+
+static menuframework_s  s_software_menu;
+static menuframework_s	s_opengl_menu;
+static menuframework_s *s_current_menu;
+static int				s_current_menu_index;
+
+static menulist_s		s_mode_list[2];
+static menulist_s		s_ref_list[2];
+static menuslider_s		s_tq_slider;
+static menuslider_s		s_screensize_slider[2];
+static menuslider_s		s_brightness_slider[2];
+static menulist_s  		s_fs_box[2];
+static menulist_s  		s_stipple_box;
+static menulist_s  		s_paletted_texture_box;
+static menulist_s  		s_windowed_mouse;
+static menuaction_s		s_apply_action[2];
+static menuaction_s		s_defaults_action[2];
+
+static void DriverCallback( void *unused )
+{
+	s_ref_list[!s_current_menu_index].curvalue = s_ref_list[s_current_menu_index].curvalue;
+
+	if ( s_ref_list[s_current_menu_index].curvalue < 2 )
+	{
+		s_current_menu = &s_software_menu;
+		s_current_menu_index = 0;
+	}
+	else
+	{
+		s_current_menu = &s_opengl_menu;
+		s_current_menu_index = 1;
+	}
+
+}
+
+static void ScreenSizeCallback( void *s )
+{
+	menuslider_s *slider = ( menuslider_s * ) s;
+
+	Cvar_SetValue( "viewsize", slider->curvalue * 10 );
+}
+
+static void BrightnessCallback( void *s )
+{
+	menuslider_s *slider = ( menuslider_s * ) s;
+
+	if ( s_current_menu_index == 0)
+		s_brightness_slider[1].curvalue = s_brightness_slider[0].curvalue;
+	else
+		s_brightness_slider[0].curvalue = s_brightness_slider[1].curvalue;
+
+	if ( stricmp( vid_ref->string, "soft" ) == 0 ||
+		 stricmp( vid_ref->string, "softx" ) == 0 )
+	{
+		float gamma = ( 0.8 - ( slider->curvalue/10.0 - 0.5 ) ) + 0.5;
+
+		Cvar_SetValue( "vid_gamma", gamma );
+	}
+}
+
+static void ResetDefaults( void *unused )
+{
+	VID_MenuInit();
+}
+
+static void ApplyChanges( void *unused )
+{
+	float gamma;
+
+	/*
+	** make values consistent
+	*/
+	s_fs_box[!s_current_menu_index].curvalue = s_fs_box[s_current_menu_index].curvalue;
+	s_brightness_slider[!s_current_menu_index].curvalue = s_brightness_slider[s_current_menu_index].curvalue;
+	s_ref_list[!s_current_menu_index].curvalue = s_ref_list[s_current_menu_index].curvalue;
+
+	/*
+	** invert sense so greater = brighter, and scale to a range of 0.5 to 1.3
+	*/
+	gamma = ( 0.8 - ( s_brightness_slider[s_current_menu_index].curvalue/10.0 - 0.5 ) ) + 0.5;
+
+	Cvar_SetValue( "vid_gamma", gamma );
+	Cvar_SetValue( "sw_stipplealpha", s_stipple_box.curvalue );
+	Cvar_SetValue( "gl_picmip", 3 - s_tq_slider.curvalue );
+	Cvar_SetValue( "vid_fullscreen", s_fs_box[s_current_menu_index].curvalue );
+	Cvar_SetValue( "gl_ext_palettedtexture", s_paletted_texture_box.curvalue );
+	Cvar_SetValue( "sw_mode", s_mode_list[SOFTWARE_MENU].curvalue );
+	Cvar_SetValue( "gl_mode", s_mode_list[OPENGL_MENU].curvalue );
+	Cvar_SetValue( "_windowed_mouse", s_windowed_mouse.curvalue);
+
+	switch ( s_ref_list[s_current_menu_index].curvalue )
+	{
+	case REF_SOFT:
+		Cvar_Set( "vid_ref", "soft" );
+		break;
+	case REF_SOFTX11:
+		Cvar_Set( "vid_ref", "softx" );
+		break;
+	case REF_OPENGL:
+		Cvar_Set( "vid_ref", "gl" );
+		Cvar_Set( "gl_driver", "opengl32" );
+		break;
+	}
+
+#if 0
+	/*
+	** update appropriate stuff if we're running OpenGL and gamma
+	** has been modified
+	*/
+	if ( stricmp( vid_ref->string, "gl" ) == 0 )
+	{
+		if ( vid_gamma->modified )
+		{
+			vid_ref->modified = true;
+			if ( stricmp( gl_driver->string, "3dfxgl" ) == 0 )
+			{
+				char envbuffer[1024];
+				float g;
+
+				vid_ref->modified = true;
+
+				g = 2.00 * ( 0.8 - ( vid_gamma->value - 0.5 ) ) + 1.0F;
+				Com_sprintf( envbuffer, sizeof(envbuffer), "SST_GAMMA=%f", g );
+				putenv( envbuffer );
+
+				vid_gamma->modified = false;
+			}
+		}
+	}
+#endif
+
+	M_ForceMenuOff();
+}
+
+/*
+** VID_MenuInit
+*/
+void VID_MenuInit( void )
+{
+	static const char *resolutions[] = 
+	{
+		"[320 240  ]",
+		"[400 300  ]",
+		"[512 384  ]",
+		"[640 480  ]",
+		"[800 600  ]",
+		"[960 720  ]",
+		"[1024 768 ]",
+		"[1152 864 ]",
+		"[1280 1024]",
+		"[1600 1200]",
+		0
+	};
+	static const char *refs[] =
+	{
+		"[software      ]",
+		"[software X11  ]",
+		"[default OpenGL]",
+		0
+	};
+	static const char *yesno_names[] =
+	{
+		"no",
+		"yes",
+		0
+	};
+	int i;
+
+	if ( !gl_driver )
+		gl_driver = Cvar_Get( "gl_driver", "opengl32", 0 );
+	if ( !gl_picmip )
+		gl_picmip = Cvar_Get( "gl_picmip", "0", 0 );
+	if ( !gl_mode )
+		gl_mode = Cvar_Get( "gl_mode", "3", 0 );
+	if ( !sw_mode )
+		sw_mode = Cvar_Get( "sw_mode", "0", 0 );
+	if ( !gl_ext_palettedtexture )
+		gl_ext_palettedtexture = Cvar_Get( "gl_ext_palettedtexture", "1", CVAR_ARCHIVE );
+
+	if ( !sw_stipplealpha )
+		sw_stipplealpha = Cvar_Get( "sw_stipplealpha", "0", CVAR_ARCHIVE );
+
+	if ( !_windowed_mouse)
+        _windowed_mouse = Cvar_Get( "_windowed_mouse", "0", CVAR_ARCHIVE );
+
+	s_mode_list[SOFTWARE_MENU].curvalue = sw_mode->value;
+	s_mode_list[OPENGL_MENU].curvalue = gl_mode->value;
+
+	if ( !scr_viewsize )
+		scr_viewsize = Cvar_Get ("viewsize", "100", CVAR_ARCHIVE);
+
+	s_screensize_slider[SOFTWARE_MENU].curvalue = scr_viewsize->value/10;
+	s_screensize_slider[OPENGL_MENU].curvalue = scr_viewsize->value/10;
+
+	if ( strcmp( vid_ref->string, "soft" ) == 0)
+	{
+		s_current_menu_index = SOFTWARE_MENU;
+		s_ref_list[0].curvalue = s_ref_list[1].curvalue = REF_SOFT;
+	}
+	else if (strcmp( vid_ref->string, "softx" ) == 0 ) 
+	{
+		s_current_menu_index = SOFTWARE_MENU;
+		s_ref_list[0].curvalue = s_ref_list[1].curvalue = REF_SOFTX11;
+	}
+	else if ( strcmp( vid_ref->string, "gl" ) == 0 )
+	{
+		s_current_menu_index = OPENGL_MENU;
+		s_ref_list[s_current_menu_index].curvalue = REF_OPENGL;
+#if 0
+		if ( strcmp( gl_driver->string, "3dfxgl" ) == 0 )
+			s_ref_list[s_current_menu_index].curvalue = REF_3DFX;
+		else if ( strcmp( gl_driver->string, "pvrgl" ) == 0 )
+			s_ref_list[s_current_menu_index].curvalue = REF_POWERVR;
+		else if ( strcmp( gl_driver->string, "opengl32" ) == 0 )
+			s_ref_list[s_current_menu_index].curvalue = REF_OPENGL;
+		else
+			s_ref_list[s_current_menu_index].curvalue = REF_VERITE;
+#endif
+	}
+
+	s_software_menu.x = viddef.width * 0.50;
+	s_software_menu.nitems = 0;
+	s_opengl_menu.x = viddef.width * 0.50;
+	s_opengl_menu.nitems = 0;
+
+	for ( i = 0; i < 2; i++ )
+	{
+		s_ref_list[i].generic.type = MTYPE_SPINCONTROL;
+		s_ref_list[i].generic.name = "driver";
+		s_ref_list[i].generic.x = 0;
+		s_ref_list[i].generic.y = 0;
+		s_ref_list[i].generic.callback = DriverCallback;
+		s_ref_list[i].itemnames = refs;
+
+		s_mode_list[i].generic.type = MTYPE_SPINCONTROL;
+		s_mode_list[i].generic.name = "video mode";
+		s_mode_list[i].generic.x = 0;
+		s_mode_list[i].generic.y = 10;
+		s_mode_list[i].itemnames = resolutions;
+
+		s_screensize_slider[i].generic.type	= MTYPE_SLIDER;
+		s_screensize_slider[i].generic.x		= 0;
+		s_screensize_slider[i].generic.y		= 20;
+		s_screensize_slider[i].generic.name	= "screen size";
+		s_screensize_slider[i].minvalue = 3;
+		s_screensize_slider[i].maxvalue = 12;
+		s_screensize_slider[i].generic.callback = ScreenSizeCallback;
+
+		s_brightness_slider[i].generic.type	= MTYPE_SLIDER;
+		s_brightness_slider[i].generic.x	= 0;
+		s_brightness_slider[i].generic.y	= 30;
+		s_brightness_slider[i].generic.name	= "brightness";
+		s_brightness_slider[i].generic.callback = BrightnessCallback;
+		s_brightness_slider[i].minvalue = 5;
+		s_brightness_slider[i].maxvalue = 13;
+		s_brightness_slider[i].curvalue = ( 1.3 - vid_gamma->value + 0.5 ) * 10;
+
+		s_fs_box[i].generic.type = MTYPE_SPINCONTROL;
+		s_fs_box[i].generic.x	= 0;
+		s_fs_box[i].generic.y	= 40;
+		s_fs_box[i].generic.name	= "fullscreen";
+		s_fs_box[i].itemnames = yesno_names;
+		s_fs_box[i].curvalue = vid_fullscreen->value;
+
+		s_defaults_action[i].generic.type = MTYPE_ACTION;
+		s_defaults_action[i].generic.name = "reset to default";
+		s_defaults_action[i].generic.x    = 0;
+		s_defaults_action[i].generic.y    = 90;
+		s_defaults_action[i].generic.callback = ResetDefaults;
+
+		s_apply_action[i].generic.type = MTYPE_ACTION;
+		s_apply_action[i].generic.name = "apply";
+		s_apply_action[i].generic.x    = 0;
+		s_apply_action[i].generic.y    = 100;
+		s_apply_action[i].generic.callback = ApplyChanges;
+	}
+
+	s_stipple_box.generic.type = MTYPE_SPINCONTROL;
+	s_stipple_box.generic.x	= 0;
+	s_stipple_box.generic.y	= 60;
+	s_stipple_box.generic.name	= "stipple alpha";
+	s_stipple_box.curvalue = sw_stipplealpha->value;
+	s_stipple_box.itemnames = yesno_names;
+
+	s_windowed_mouse.generic.type = MTYPE_SPINCONTROL;
+	s_windowed_mouse.generic.x  = 0;
+	s_windowed_mouse.generic.y  = 72;
+	s_windowed_mouse.generic.name   = "windowed mouse";
+	s_windowed_mouse.curvalue = _windowed_mouse->value;
+	s_windowed_mouse.itemnames = yesno_names;
+
+	s_tq_slider.generic.type	= MTYPE_SLIDER;
+	s_tq_slider.generic.x		= 0;
+	s_tq_slider.generic.y		= 60;
+	s_tq_slider.generic.name	= "texture quality";
+	s_tq_slider.minvalue = 0;
+	s_tq_slider.maxvalue = 3;
+	s_tq_slider.curvalue = 3-gl_picmip->value;
+
+	s_paletted_texture_box.generic.type = MTYPE_SPINCONTROL;
+	s_paletted_texture_box.generic.x	= 0;
+	s_paletted_texture_box.generic.y	= 70;
+	s_paletted_texture_box.generic.name	= "8-bit textures";
+	s_paletted_texture_box.itemnames = yesno_names;
+	s_paletted_texture_box.curvalue = gl_ext_palettedtexture->value;
+
+	Menu_AddItem( &s_software_menu, ( void * ) &s_ref_list[SOFTWARE_MENU] );
+	Menu_AddItem( &s_software_menu, ( void * ) &s_mode_list[SOFTWARE_MENU] );
+	Menu_AddItem( &s_software_menu, ( void * ) &s_screensize_slider[SOFTWARE_MENU] );
+	Menu_AddItem( &s_software_menu, ( void * ) &s_brightness_slider[SOFTWARE_MENU] );
+	Menu_AddItem( &s_software_menu, ( void * ) &s_fs_box[SOFTWARE_MENU] );
+	Menu_AddItem( &s_software_menu, ( void * ) &s_stipple_box );
+	Menu_AddItem( &s_software_menu, ( void * ) &s_windowed_mouse );
+
+	Menu_AddItem( &s_opengl_menu, ( void * ) &s_ref_list[OPENGL_MENU] );
+	Menu_AddItem( &s_opengl_menu, ( void * ) &s_mode_list[OPENGL_MENU] );
+	Menu_AddItem( &s_opengl_menu, ( void * ) &s_screensize_slider[OPENGL_MENU] );
+	Menu_AddItem( &s_opengl_menu, ( void * ) &s_brightness_slider[OPENGL_MENU] );
+	Menu_AddItem( &s_opengl_menu, ( void * ) &s_fs_box[OPENGL_MENU] );
+	Menu_AddItem( &s_opengl_menu, ( void * ) &s_tq_slider );
+	Menu_AddItem( &s_opengl_menu, ( void * ) &s_paletted_texture_box );
+
+	Menu_AddItem( &s_software_menu, ( void * ) &s_defaults_action[SOFTWARE_MENU] );
+	Menu_AddItem( &s_software_menu, ( void * ) &s_apply_action[SOFTWARE_MENU] );
+	Menu_AddItem( &s_opengl_menu, ( void * ) &s_defaults_action[OPENGL_MENU] );
+	Menu_AddItem( &s_opengl_menu, ( void * ) &s_apply_action[OPENGL_MENU] );
+
+	Menu_Center( &s_software_menu );
+	Menu_Center( &s_opengl_menu );
+	s_opengl_menu.x -= 8;
+	s_software_menu.x -= 8;
+}
+
+/*
+================
+VID_MenuDraw
+================
+*/
+void VID_MenuDraw (void)
+{
+	int w, h;
+
+	if ( s_current_menu_index == 0 )
+		s_current_menu = &s_software_menu;
+	else
+		s_current_menu = &s_opengl_menu;
+
+	/*
+	** draw the banner
+	*/
+	re.DrawGetPicSize( &w, &h, "m_banner_video" );
+	re.DrawPic( viddef.width / 2 - w / 2, viddef.height /2 - 110, "m_banner_video" );
+
+	/*
+	** move cursor to a reasonable starting position
+	*/
+	Menu_AdjustCursor( s_current_menu, 1 );
+
+	/*
+	** draw the menu
+	*/
+	Menu_Draw( s_current_menu );
+}
+
+/*
+================
+VID_MenuKey
+================
+*/
+const char *VID_MenuKey( int key )
+{
+	extern void M_PopMenu( void );
+
+	menuframework_s *m = s_current_menu;
+	static const char *sound = "misc/menu1.wav";
+
+	switch ( key )
+	{
+	case K_ESCAPE:
+		M_PopMenu();
+		return NULL;
+	case K_UPARROW:
+		m->cursor--;
+		Menu_AdjustCursor( m, -1 );
+		break;
+	case K_DOWNARROW:
+		m->cursor++;
+		Menu_AdjustCursor( m, 1 );
+		break;
+	case K_LEFTARROW:
+		Menu_SlideItem( m, -1 );
+		break;
+	case K_RIGHTARROW:
+		Menu_SlideItem( m, 1 );
+		break;
+	case K_ENTER:
+		Menu_SelectItem( m );
+		break;
+	}
+
+	return sound;
+}
+
+
--- /dev/null
+++ b/plan9/vid_so.c
@@ -1,0 +1,489 @@
+// Main windowed and fullscreen graphics interface module. This module
+// is used for both the software and OpenGL rendering versions of the
+// Quake refresh engine.
+
+#define SO_FILE "/etc/quake2.conf"
+
+#include <assert.h>
+#include <dlfcn.h> // ELF dl loader
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "../client/client.h"
+
+#include "../linux/rw_linux.h"
+
+// Structure containing functions exported from refresh DLL
+refexport_t	re;
+
+// Console variables that we need to access from this module
+cvar_t		*vid_gamma;
+cvar_t		*vid_ref;			// Name of Refresh DLL loaded
+cvar_t		*vid_xpos;			// X coordinate of window position
+cvar_t		*vid_ypos;			// Y coordinate of window position
+cvar_t		*vid_fullscreen;
+
+// Global variables used internally by this module
+viddef_t	viddef;				// global video state; used by other modules
+void		*reflib_library;		// Handle to refresh DLL 
+qboolean	reflib_active = 0;
+
+#define VID_NUM_MODES ( sizeof( vid_modes ) / sizeof( vid_modes[0] ) )
+
+/** KEYBOARD **************************************************************/
+
+void Do_Key_Event(int key, qboolean down);
+
+void (*KBD_Update_fp)(void);
+void (*KBD_Init_fp)(Key_Event_fp_t fp);
+void (*KBD_Close_fp)(void);
+
+/** MOUSE *****************************************************************/
+
+in_state_t in_state;
+
+void (*RW_IN_Init_fp)(in_state_t *in_state_p);
+void (*RW_IN_Shutdown_fp)(void);
+void (*RW_IN_Activate_fp)(qboolean active);
+void (*RW_IN_Commands_fp)(void);
+void (*RW_IN_Move_fp)(usercmd_t *cmd);
+void (*RW_IN_Frame_fp)(void);
+
+void Real_IN_Init (void);
+
+/*
+==========================================================================
+
+DLL GLUE
+
+==========================================================================
+*/
+
+#define	MAXPRINTMSG	4096
+void VID_Printf (int print_level, char *fmt, ...)
+{
+	va_list		argptr;
+	char		msg[MAXPRINTMSG];
+	static qboolean	inupdate;
+	
+	va_start (argptr,fmt);
+	vsprintf (msg,fmt,argptr);
+	va_end (argptr);
+
+	if (print_level == PRINT_ALL)
+		Com_Printf ("%s", msg);
+	else
+		Com_DPrintf ("%s", msg);
+}
+
+void VID_Error (int err_level, char *fmt, ...)
+{
+	va_list		argptr;
+	char		msg[MAXPRINTMSG];
+	static qboolean	inupdate;
+	
+	va_start (argptr,fmt);
+	vsprintf (msg,fmt,argptr);
+	va_end (argptr);
+
+	Com_Error (err_level,"%s", msg);
+}
+
+//==========================================================================
+
+/*
+============
+VID_Restart_f
+
+Console command to re-start the video mode and refresh DLL. We do this
+simply by setting the modified flag for the vid_ref variable, which will
+cause the entire video mode and refresh DLL to be reset on the next frame.
+============
+*/
+void VID_Restart_f (void)
+{
+	vid_ref->modified = true;
+}
+
+/*
+** VID_GetModeInfo
+*/
+typedef struct vidmode_s
+{
+	const char *description;
+	int         width, height;
+	int         mode;
+} vidmode_t;
+
+vidmode_t vid_modes[] =
+{
+	{ "Mode 0: 320x240",   320, 240,   0 },
+	{ "Mode 1: 400x300",   400, 300,   1 },
+	{ "Mode 2: 512x384",   512, 384,   2 },
+	{ "Mode 3: 640x480",   640, 480,   3 },
+	{ "Mode 4: 800x600",   800, 600,   4 },
+	{ "Mode 5: 960x720",   960, 720,   5 },
+	{ "Mode 6: 1024x768",  1024, 768,  6 },
+	{ "Mode 7: 1152x864",  1152, 864,  7 },
+	{ "Mode 8: 1280x1024",  1280, 1024, 8 },
+	{ "Mode 9: 1600x1200", 1600, 1200, 9 }
+};
+
+qboolean VID_GetModeInfo( int *width, int *height, int mode )
+{
+	if ( mode < 0 || mode >= VID_NUM_MODES )
+		return false;
+
+	*width  = vid_modes[mode].width;
+	*height = vid_modes[mode].height;
+
+	return true;
+}
+
+/*
+** VID_NewWindow
+*/
+void VID_NewWindow ( int width, int height)
+{
+	viddef.width  = width;
+	viddef.height = height;
+}
+
+void VID_FreeReflib (void)
+{
+	if (reflib_library) {
+		if (KBD_Close_fp)
+			KBD_Close_fp();
+		if (RW_IN_Shutdown_fp)
+			RW_IN_Shutdown_fp();
+		dlclose(reflib_library);
+	}
+
+	KBD_Init_fp = NULL;
+	KBD_Update_fp = NULL;
+	KBD_Close_fp = NULL;
+	RW_IN_Init_fp = NULL;
+	RW_IN_Shutdown_fp = NULL;
+	RW_IN_Activate_fp = NULL;
+	RW_IN_Commands_fp = NULL;
+	RW_IN_Move_fp = NULL;
+	RW_IN_Frame_fp = NULL;
+
+	memset (&re, 0, sizeof(re));
+	reflib_library = NULL;
+	reflib_active  = false;
+}
+
+/*
+==============
+VID_LoadRefresh
+==============
+*/
+qboolean VID_LoadRefresh( char *name )
+{
+	refimport_t	ri;
+	GetRefAPI_t	GetRefAPI;
+	char	fn[MAX_OSPATH];
+	struct stat st;
+	extern uid_t saved_euid;
+	FILE *fp;
+	
+	if ( reflib_active )
+	{
+		if (KBD_Close_fp)
+			KBD_Close_fp();
+		if (RW_IN_Shutdown_fp)
+			RW_IN_Shutdown_fp();
+		KBD_Close_fp = NULL;
+		RW_IN_Shutdown_fp = NULL;
+		re.Shutdown();
+		VID_FreeReflib ();
+	}
+
+	Com_Printf( "------- Loading %s -------\n", name );
+
+	//regain root
+	seteuid(saved_euid);
+
+	if ((fp = fopen(SO_FILE, "r")) == NULL) {
+		Com_Printf( "LoadLibrary(\"%s\") failed: can't open " SO_FILE " (required for location of ref libraries)\n", name);
+		return false;
+	}
+	fgets(fn, sizeof(fn), fp);
+	fclose(fp);
+	if (*fn && fn[strlen(fn) - 1] == '\n')
+		fn[strlen(fn) - 1] = 0;
+
+	strcat(fn, "/");
+	strcat(fn, name);
+
+	// permission checking
+	if (strstr(fn, "softx") == NULL) { // softx doesn't require root
+		if (stat(fn, &st) == -1) {
+			Com_Printf( "LoadLibrary(\"%s\") failed: %s\n", name, strerror(errno));
+			return false;
+		}
+		if (st.st_uid != 0) {
+			Com_Printf( "LoadLibrary(\"%s\") failed: ref is not owned by root\n", name);
+			return false;
+		}
+#if 0
+		if ((st.st_mode & 0777) & ~0700) {
+			Com_Printf( "LoadLibrary(\"%s\") failed: invalid permissions, must be 700 for security considerations\n", name);
+			return false;
+		}
+#endif
+	} else {
+		// softx requires we give up root now
+		setreuid(getuid(), getuid());
+		setegid(getgid());
+	}
+
+	if ( ( reflib_library = dlopen( fn, RTLD_NOW ) ) == 0 )
+	{
+		Com_Printf( "LoadLibrary(\"%s\") failed: %s\n", name , dlerror());
+		return false;
+	}
+
+	ri.Cmd_AddCommand = Cmd_AddCommand;
+	ri.Cmd_RemoveCommand = Cmd_RemoveCommand;
+	ri.Cmd_Argc = Cmd_Argc;
+	ri.Cmd_Argv = Cmd_Argv;
+	ri.Cmd_ExecuteText = Cbuf_ExecuteText;
+	ri.Con_Printf = VID_Printf;
+	ri.Sys_Error = VID_Error;
+	ri.FS_LoadFile = FS_LoadFile;
+	ri.FS_FreeFile = FS_FreeFile;
+	ri.FS_Gamedir = FS_Gamedir;
+	ri.Cvar_Get = Cvar_Get;
+	ri.Cvar_Set = Cvar_Set;
+	ri.Cvar_SetValue = Cvar_SetValue;
+	ri.Vid_GetModeInfo = VID_GetModeInfo;
+	ri.Vid_MenuInit = VID_MenuInit;
+	ri.Vid_NewWindow = VID_NewWindow;
+
+	if ( ( GetRefAPI = (void *) dlsym( reflib_library, "GetRefAPI" ) ) == 0 )
+		Com_Error( ERR_FATAL, "dlsym failed on %s", name );
+
+	re = GetRefAPI( ri );
+
+	if (re.api_version != API_VERSION)
+	{
+		VID_FreeReflib ();
+		Com_Error (ERR_FATAL, "%s has incompatible api_version", name);
+	}
+
+	/* Init IN (Mouse) */
+	in_state.IN_CenterView_fp = IN_CenterView;
+	in_state.Key_Event_fp = Do_Key_Event;
+	in_state.viewangles = cl.viewangles;
+	in_state.in_strafe_state = &in_strafe.state;
+
+	if ((RW_IN_Init_fp = dlsym(reflib_library, "RW_IN_Init")) == NULL ||
+		(RW_IN_Shutdown_fp = dlsym(reflib_library, "RW_IN_Shutdown")) == NULL ||
+		(RW_IN_Activate_fp = dlsym(reflib_library, "RW_IN_Activate")) == NULL ||
+		(RW_IN_Commands_fp = dlsym(reflib_library, "RW_IN_Commands")) == NULL ||
+		(RW_IN_Move_fp = dlsym(reflib_library, "RW_IN_Move")) == NULL ||
+		(RW_IN_Frame_fp = dlsym(reflib_library, "RW_IN_Frame")) == NULL)
+		Sys_Error("No RW_IN functions in REF.\n");
+
+	Real_IN_Init();
+
+	if ( re.Init( 0, 0 ) == -1 )
+	{
+		re.Shutdown();
+		VID_FreeReflib ();
+		return false;
+	}
+
+	/* Init KBD */
+#if 1
+	if ((KBD_Init_fp = dlsym(reflib_library, "KBD_Init")) == NULL ||
+		(KBD_Update_fp = dlsym(reflib_library, "KBD_Update")) == NULL ||
+		(KBD_Close_fp = dlsym(reflib_library, "KBD_Close")) == NULL)
+		Sys_Error("No KBD functions in REF.\n");
+#else
+	{
+		void KBD_Init(void);
+		void KBD_Update(void);
+		void KBD_Close(void);
+
+		KBD_Init_fp = KBD_Init;
+		KBD_Update_fp = KBD_Update;
+		KBD_Close_fp = KBD_Close;
+	}
+#endif
+	KBD_Init_fp(Do_Key_Event);
+
+	// give up root now
+	setreuid(getuid(), getuid());
+	setegid(getgid());
+
+	Com_Printf( "------------------------------------\n");
+	reflib_active = true;
+	return true;
+}
+
+/*
+============
+VID_CheckChanges
+
+This function gets called once just before drawing each frame, and it's sole purpose in life
+is to check to see if any of the video mode parameters have changed, and if they have to 
+update the rendering DLL and/or video mode to match.
+============
+*/
+void VID_CheckChanges (void)
+{
+	char name[100];
+	cvar_t *sw_mode;
+
+	if ( vid_ref->modified )
+	{
+		S_StopAllSounds();
+	}
+
+	while (vid_ref->modified)
+	{
+		/*
+		** refresh has changed
+		*/
+		vid_ref->modified = false;
+		vid_fullscreen->modified = true;
+		cl.refresh_prepped = false;
+		cls.disable_screen = true;
+
+		sprintf( name, "ref_%s.so", vid_ref->string );
+		if ( !VID_LoadRefresh( name ) )
+		{
+			if ( strcmp (vid_ref->string, "soft") == 0 ||
+				strcmp (vid_ref->string, "softx") == 0 ) {
+Com_Printf("Refresh failed\n");
+				sw_mode = Cvar_Get( "sw_mode", "0", 0 );
+				if (sw_mode->value != 0) {
+Com_Printf("Trying mode 0\n");
+					Cvar_SetValue("sw_mode", 0);
+					if ( !VID_LoadRefresh( name ) )
+						Com_Error (ERR_FATAL, "Couldn't fall back to software refresh!");
+				} else
+					Com_Error (ERR_FATAL, "Couldn't fall back to software refresh!");
+			}
+
+			Cvar_Set( "vid_ref", "soft" );
+
+			/*
+			** drop the console if we fail to load a refresh
+			*/
+			if ( cls.key_dest != key_console )
+			{
+				Con_ToggleConsole_f();
+			}
+		}
+		cls.disable_screen = false;
+	}
+
+}
+
+/*
+============
+VID_Init
+============
+*/
+void VID_Init (void)
+{
+	/* Create the video variables so we know how to start the graphics drivers */
+	// if DISPLAY is defined, try X
+	if (getenv("DISPLAY"))
+		vid_ref = Cvar_Get ("vid_ref", "softx", CVAR_ARCHIVE);
+	else
+		vid_ref = Cvar_Get ("vid_ref", "soft", CVAR_ARCHIVE);
+	vid_xpos = Cvar_Get ("vid_xpos", "3", CVAR_ARCHIVE);
+	vid_ypos = Cvar_Get ("vid_ypos", "22", CVAR_ARCHIVE);
+	vid_fullscreen = Cvar_Get ("vid_fullscreen", "0", CVAR_ARCHIVE);
+	vid_gamma = Cvar_Get( "vid_gamma", "1", CVAR_ARCHIVE );
+
+	/* Add some console commands that we want to handle */
+	Cmd_AddCommand ("vid_restart", VID_Restart_f);
+
+	/* Disable the 3Dfx splash screen */
+	putenv("FX_GLIDE_NO_SPLASH=0");
+		
+	/* Start the graphics mode and load refresh DLL */
+	VID_CheckChanges();
+}
+
+/*
+============
+VID_Shutdown
+============
+*/
+void VID_Shutdown (void)
+{
+	if ( reflib_active )
+	{
+		if (KBD_Close_fp)
+			KBD_Close_fp();
+		if (RW_IN_Shutdown_fp)
+			RW_IN_Shutdown_fp();
+		KBD_Close_fp = NULL;
+		RW_IN_Shutdown_fp = NULL;
+		re.Shutdown ();
+		VID_FreeReflib ();
+	}
+}
+
+
+/*****************************************************************************/
+/* INPUT                                                                     */
+/*****************************************************************************/
+
+cvar_t	*in_joystick;
+
+// This if fake, it's acutally done by the Refresh load
+void IN_Init (void)
+{
+	in_joystick	= Cvar_Get ("in_joystick", "0", CVAR_ARCHIVE);
+}
+
+void Real_IN_Init (void)
+{
+	if (RW_IN_Init_fp)
+		RW_IN_Init_fp(&in_state);
+}
+
+void IN_Shutdown (void)
+{
+	if (RW_IN_Shutdown_fp)
+		RW_IN_Shutdown_fp();
+}
+
+void IN_Commands (void)
+{
+	if (RW_IN_Commands_fp)
+		RW_IN_Commands_fp();
+}
+
+void IN_Move (usercmd_t *cmd)
+{
+	if (RW_IN_Move_fp)
+		RW_IN_Move_fp(cmd);
+}
+
+void IN_Frame (void)
+{
+	if (RW_IN_Frame_fp)
+		RW_IN_Frame_fp();
+}
+
+void IN_Activate (qboolean active)
+{
+	if (RW_IN_Activate_fp)
+		RW_IN_Activate_fp(active);
+}
+
+void Do_Key_Event(int key, qboolean down)
+{
+	Key_Event(key, down, Sys_Milliseconds());
+}
+