This is an automated email from the git hooks/post-receive script.

smcv pushed a commit to annotated tag debian/7.10+ctf1.05_dfsg-1
in repository yquake2.

commit ec97f12c8d5b898fb4652c4e60b3d38409f612ef
Author: Simon McVittie <[email protected]>
Date:   Sun Dec 10 19:55:14 2017 +0000

    New upstream version 7.10+ctf1.05~dfsg
---
 CHANGELOG                         |  15 +
 CMakeLists.txt                    |   3 +
 Makefile                          |   6 +-
 src/backends/generic/vid.c        |  23 +-
 src/backends/sdl/input.c          | 724 +++++++++++++++++++++++++++++++++++++-
 src/backends/sdl/refresh.c        |  32 +-
 src/backends/unix/system.c        |  49 ++-
 src/backends/windows/system.c     |   2 +-
 src/client/cl_cin.c               |  17 +-
 src/client/cl_keyboard.c          |  58 ++-
 src/client/cl_main.c              |  10 +-
 src/client/cl_prediction.c        |   4 +-
 src/client/cl_screen.c            |   2 +-
 src/client/cl_tempentities.c      |  20 +-
 src/client/header/client.h        |   6 +-
 src/client/header/keyboard.h      |  14 +-
 src/client/menu/menu.c            | 280 +++++++++------
 src/client/menu/videomenu.c       |  11 +-
 src/client/refresh/files/pcx.c    |  64 +++-
 src/client/refresh/gl/r_main.c    |  11 +
 src/client/refresh/gl/r_sdl.c     |  13 +
 src/client/refresh/gl3/gl3_main.c |  11 +
 src/client/refresh/gl3/gl3_sdl.c  |  16 +-
 src/client/sound/ogg.c            |  21 +-
 src/client/sound/sound.c          |  10 +
 src/common/collision.c            |  24 +-
 src/common/frame.c                |  16 +
 src/common/header/common.h        |   2 +-
 src/common/header/shared.h        |   4 +-
 29 files changed, 1264 insertions(+), 204 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 17aeb32..862e844 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,18 @@
+Quake II 7.02 to 7.10:
+- Joystick support including haptic feedback. This fantastic work was
+  done by Denis Pauk. The dirty work is done by SDL, how good or bad
+  a joystick or gamepad is supported depends on SDLs support for it.
+- Fix the old SDL sound backend, s_openal set to 0 is working again.
+- Fix possible Vorbis buffer underruns if too many sound samples are
+  in flight. This occured only in large multi player games with at
+  least 6 custom models.
+- Fix a possible crash on Windows if MSAA was set to a value not
+  supported by the driver.
+- It's now possible to play through the whole game on a Raspberry PI
+  and other ARM boards. Please note that the RPIs hardware is really
+  limited. Only the OpenGL 1.4 renderer is supported and the framerate
+  is highly dependend on the screen resolution.
+
 Quake II 7.01 to 7.02:
 - Fix several corner cases regarding render library loading. The game
   should now always fall back to the OpenGL 1.4 renderer if the new
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c836f02..84e260d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -531,6 +531,7 @@ set_target_properties(game PROPERTIES
                PREFIX ""
                LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release/baseq2
                RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release/baseq2
+               SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}
                )
 target_link_libraries(game ${yquake2LinkerFlags})
 
@@ -540,6 +541,7 @@ set_target_properties(ref_gl1 PROPERTIES
                PREFIX ""
                LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release
                RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release
+               SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}
                )
 target_link_libraries(ref_gl1 ${yquake2LinkerFlags} 
${yquake2OpenGLLinkerFlags} ${yquake2SDLLinkerFlags})
 
@@ -549,5 +551,6 @@ set_target_properties(ref_gl3 PROPERTIES
                PREFIX ""
                LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release
                RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release
+               SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}
                )
 target_link_libraries(ref_gl3 ${yquake2LinkerFlags} ${yquake2SDLLinkerFlags})
diff --git a/Makefile b/Makefile
index 801db08..a34985f 100755
--- a/Makefile
+++ b/Makefile
@@ -277,7 +277,7 @@ endif
 CFLAGS += -fvisibility=hidden
 LDFLAGS += -fvisibility=hidden
 
-ifneq ($(YQ2_OSTYPE), $(filter $(YQ2_OSTYPE), Darwin, OpenBSD))
+ifneq ($(YQ2_OSTYPE), $(filter $(YQ2_OSTYPE), Darwin OpenBSD))
 # for some reason the OSX & OpenBSD linker doesn't support this
 LDFLAGS += -Wl,--no-undefined
 endif
@@ -451,7 +451,7 @@ ifeq ($(YQ2_OSTYPE), OpenBSD)
 release/quake2 : CFLAGS += -DUSE_OPENAL 
-DDEFAULT_OPENAL_DRIVER='"libopenal.so"' -DDLOPEN_OPENAL
 else ifeq ($(YQ2_OSTYPE), Darwin)
 release/quake2 : CFLAGS += -DUSE_OPENAL 
-DDEFAULT_OPENAL_DRIVER='"libopenal.dylib"' 
-I/usr/local/opt/openal-soft/include -DDLOPEN_OPENAL
-release/quake2 : LDFLAGS += -L/usr/local/opt/openal-soft/lib
+release/quake2 : LDFLAGS += -L/usr/local/opt/openal-soft/lib -rpath 
/usr/local/opt/openal-soft/lib
 else
 release/quake2 : CFLAGS += -DUSE_OPENAL 
-DDEFAULT_OPENAL_DRIVER='"libopenal.so.1"' -DDLOPEN_OPENAL
 endif
@@ -460,7 +460,7 @@ release/quake2 : CFLAGS += -DUSE_OPENAL
 release/quake2 : LDFLAGS += -lopenal
 ifeq ($(YQ2_OSTYPE), Darwin)
 release/quake2 : CFLAGS += -I/usr/local/opt/openal-soft/include
-release/quake2 : LDFLAGS += -L/usr/local/opt/openal-soft/lib
+release/quake2 : LDFLAGS += -L/usr/local/opt/openal-soft/lib -rpath 
/usr/local/opt/openal-soft/lib
 endif # Darwin
 endif # !DLOPEN_OPENAL
 endif # WITH_OPENAL
diff --git a/src/backends/generic/vid.c b/src/backends/generic/vid.c
index 76f6a9c..8a653b6 100644
--- a/src/backends/generic/vid.c
+++ b/src/backends/generic/vid.c
@@ -101,6 +101,14 @@ vidmode_t vid_modes[] = {
        {"Mode 21: 1920x1080", 1920, 1080, 21},
        {"Mode 22: 1920x1200", 1920, 1200, 22},
        {"Mode 23: 2048x1536", 2048, 1536, 23},
+       {"Mode 24: 2560x1080", 2560, 1080, 24},
+       {"Mode 25: 2560x1440", 2560, 1440, 25},
+       {"Mode 26: 2560x1600", 2560, 1600, 26},
+       {"Mode 27: 3440x1440", 3440, 1440, 27},
+       {"Mode 28: 3840x1600", 3840, 1600, 28},
+       {"Mode 29: 3840x2160", 3840, 2160, 29},
+       {"Mode 30: 4096x2160", 4096, 2160, 30},
+       {"Mode 31: 5120x2880", 5120, 2880, 31},
 };
 
 /* Console variables that we need to access from this module */
@@ -177,11 +185,18 @@ VID_CheckChanges(void)
                cls.disable_screen = true;
 
                // Proceed to reboot the refresher
-               if(!VID_LoadRefresh() && (strcmp(vid_renderer->string, "gl1") 
!= 0))
+               if(!VID_LoadRefresh())
                {
-                       Com_Printf("\n ... trying again with standard OpenGL1.x 
renderer ... \n\n");
-                       Cvar_Set("vid_renderer", "gl1");
-                       VID_LoadRefresh();
+                       if (strcmp(vid_renderer->string, "gl1") != 0)
+                       {
+                               Com_Printf("\n ... trying again with standard 
OpenGL1.x renderer ... \n\n");
+                               Cvar_Set("vid_renderer", "gl1");
+                               VID_LoadRefresh();
+                       }
+                       else
+                       {
+                               Com_Error(ERR_FATAL, "Couldn't load a rendering 
backend!\n");
+                       }
                }
                cls.disable_screen = false;
        }
diff --git a/src/backends/sdl/input.c b/src/backends/sdl/input.c
index 2034617..23233bb 100644
--- a/src/backends/sdl/input.c
+++ b/src/backends/sdl/input.c
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2010 Yamagi Burmeister
- * Copyright (C) 1997-2001 Id Software, Inc.
+ * Copyright (C) 1997-2005 Id Software, Inc.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -23,6 +23,8 @@
  * This is the Quake II input system backend, implemented with SDL.
  *
  * =======================================================================
+ *
+ * Joystick threshold code is partially based on http://ioquake3.org code.
  */
 
 #include "../../client/header/keyboard.h"
@@ -61,16 +63,65 @@
 
 #define MOUSE_MAX 3000
 #define MOUSE_MIN 40
- 
+
 /* Globals */
 static int mouse_x, mouse_y;
 static int old_mouse_x, old_mouse_y;
 static qboolean mlooking;
 
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+static float joystick_yaw, joystick_pitch;
+static float joystick_forwardmove, joystick_sidemove;
+static float joystick_up;
+static int back_button_id = -1;
+static char last_hat = SDL_HAT_CENTERED;
+static qboolean left_trigger = false;
+static qboolean right_trigger = false;
+qboolean show_haptic = false;
+
+/* Haptic feedback types */
+enum QHARPICTYPES {
+       HAPTIC_EFFECT_UNKNOWN = -1,
+       HAPTIC_EFFECT_BLASTER = 0,
+       HAPTIC_EFFECT_MENY,
+       HAPTIC_EFFECT_HYPER_BLASTER,
+       HAPTIC_EFFECT_MACHINEGUN,
+       HAPTIC_EFFECT_SHOTGUN,
+       HAPTIC_EFFECT_SSHOTGUN,
+       HAPTIC_EFFECT_RAILGUN,
+       HAPTIC_EFFECT_ROCKETGUN,
+       HAPTIC_EFFECT_GRENADE,
+       HAPTIC_EFFECT_BFG,
+       HAPTIC_EFFECT_PALANX,
+       HAPTIC_EFFECT_IONRIPPER,
+       HAPTIC_EFFECT_ETFRIFLE,
+       HAPTIC_EFFECT_SHOTGUN2,
+       HAPTIC_EFFECT_TRACKER,
+       HAPTIC_EFFECT_PAIN,
+       HAPTIC_EFFECT_STEP,
+       HAPTIC_EFFECT_TRAPCOCK,
+       HAPTIC_EFFECT_LAST
+};
+
+struct hapric_effects_cache {
+    int effect_type;
+    int effect_id;
+};
+
+static int last_haptic_volume = 0;
+static struct hapric_effects_cache last_haptic_efffect[HAPTIC_EFFECT_LAST];
+static int last_haptic_efffect_size = HAPTIC_EFFECT_LAST;
+static int last_haptic_efffect_pos = 0;
+
+/* Joystick */
+static SDL_Haptic *joystick_haptic = NULL;
+static SDL_Joystick *joystick = NULL;
+static SDL_GameController *controller = NULL;
+#endif
+
 /* CVars */
 cvar_t *vid_fullscreen;
 static cvar_t *in_grab;
-static cvar_t *in_mouse;
 static cvar_t *exponential_speedup;
 cvar_t *freelook;
 cvar_t *lookstrafe;
@@ -78,10 +129,35 @@ cvar_t *m_forward;
 static cvar_t *m_filter;
 cvar_t *m_pitch;
 cvar_t *m_side;
+cvar_t *m_up;
 cvar_t *m_yaw;
 cvar_t *sensitivity;
 static cvar_t *windowed_mouse;
 
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+/* Joystick sensitivity */
+static cvar_t *joy_yawsensitivity;
+static cvar_t *joy_pitchsensitivity;
+static cvar_t *joy_forwardsensitivity;
+static cvar_t *joy_sidesensitivity;
+static cvar_t *joy_upsensitivity;
+/* Joystick direction settings */
+static cvar_t *joy_axis_leftx;
+static cvar_t *joy_axis_lefty;
+static cvar_t *joy_axis_rightx;
+static cvar_t *joy_axis_righty;
+static cvar_t *joy_axis_triggerleft;
+static cvar_t *joy_axis_triggerright;
+/* Joystick threshold settings */
+static cvar_t *joy_axis_leftx_threshold;
+static cvar_t *joy_axis_lefty_threshold;
+static cvar_t *joy_axis_rightx_threshold;
+static cvar_t *joy_axis_righty_threshold;
+static cvar_t *joy_axis_triggerleft_threshold;
+static cvar_t *joy_axis_triggerright_threshold;
+/* Joystick haptic */
+static cvar_t *joy_haptic_magnitude;
+#endif
 
 extern void GLimp_GrabInput(qboolean grab);
 
@@ -447,10 +523,162 @@ IN_Update(void)
                                }
 #endif
                                break;
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+                       case SDL_CONTROLLERBUTTONUP:
+                       case SDL_CONTROLLERBUTTONDOWN: /* Handle Controller 
Back button */
+                       {
+                               qboolean down = (event.type == 
SDL_CONTROLLERBUTTONDOWN);
+                               if(event.cbutton.button == 
SDL_CONTROLLER_BUTTON_BACK) {
+                                       Key_Event(K_JOY_BACK, down, true);
+                               }
+                       }
+                               break;
+                       case SDL_CONTROLLERAXISMOTION:  /* Handle Controller 
Motion */
+                       {
+                               char* direction_type;
+                               float threshold = 0;
+                               float fix_value = 0;
+                               int axis_value = event.caxis.value;
+                               switch (event.caxis.axis)
+                               {
+                                       /* left/right */
+                                       case SDL_CONTROLLER_AXIS_LEFTX:
+                                               direction_type = 
joy_axis_leftx->string;
+                                               threshold = 
joy_axis_leftx_threshold->value;
+                                               break;
+                                       /* top/bottom */
+                                       case SDL_CONTROLLER_AXIS_LEFTY:
+                                               direction_type = 
joy_axis_lefty->string;
+                                               threshold = 
joy_axis_lefty_threshold->value;
+                                               break;
+                                       /* second left/right */
+                                       case SDL_CONTROLLER_AXIS_RIGHTX:
+                                               direction_type = 
joy_axis_rightx->string;
+                                               threshold = 
joy_axis_rightx_threshold->value;
+                                               break;
+                                       /* second top/bottom */
+                                       case SDL_CONTROLLER_AXIS_RIGHTY:
+                                               direction_type = 
joy_axis_righty->string;
+                                               threshold = 
joy_axis_righty_threshold->value;
+                                               break;
+                                       case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
+                                               direction_type = 
joy_axis_triggerleft->string;
+                                               threshold = 
joy_axis_triggerleft_threshold->value;
+                                               break;
+                                       case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
+                                               direction_type = 
joy_axis_triggerright->string;
+                                               threshold = 
joy_axis_triggerright_threshold->value;
+                                               break;
+                                       default:
+                                               direction_type = "none";
+                               }
+
+                               if (threshold > 0.9)
+                                       threshold = 0.9;
+
+                               if (axis_value < 0 && (axis_value > (32768 * 
threshold)))
+                                       axis_value = 0;
+                               else if (axis_value > 0 && (axis_value < (32768 
* threshold)))
+                                       axis_value = 0;
+
+                               // Smoothly ramp from dead zone to maximum 
value (from ioquake)
+                               // 
https://github.com/ioquake/ioq3/blob/master/code/sdl/sdl_input.c
+                               fix_value = ((float)abs(axis_value) / 32767.0f 
- threshold) / (1.0f - threshold);
+                               if (fix_value < 0.0f)
+                                       fix_value = 0.0f;
+
+                               axis_value = (int)(32767 * ((axis_value < 0) ? 
-fix_value : fix_value));
+
+                               if (cls.key_dest == key_game && 
(int)cl_paused->value == 0)
+                               {
+                                       if (strcmp(direction_type, "sidemove") 
== 0)
+                                       {
+                                               joystick_sidemove = axis_value 
* joy_sidesensitivity->value;
+                                               // We need to be twice faster 
because with joystic we run...
+                                               joystick_sidemove *= 
cl_sidespeed->value * 2.0f;
+                                       }
+                                       else if (strcmp(direction_type, 
"forwardmove") == 0)
+                                       {
+                                               joystick_forwardmove = 
axis_value * joy_forwardsensitivity->value;
+                                               // We need to be twice faster 
because with joystic we run...
+                                               joystick_forwardmove *= 
cl_forwardspeed->value * 2.0f;
+                                       }
+                                       else if (strcmp(direction_type, "yaw") 
== 0)
+                                       {
+                                               joystick_yaw = axis_value * 
joy_yawsensitivity->value;
+                                               joystick_yaw *= 
cl_yawspeed->value;
+                                       }
+                                       else if (strcmp(direction_type, 
"pitch") == 0)
+                                       {
+                                               joystick_pitch = axis_value * 
joy_pitchsensitivity->value;
+                                               joystick_pitch *= 
cl_pitchspeed->value;
+                                       }
+                                       else if (strcmp(direction_type, 
"updown") == 0)
+                                       {
+                                               joystick_up = axis_value * 
joy_upsensitivity->value;
+                                               joystick_up *= 
cl_upspeed->value;
+                                       }
+                               }
+
+                               if (strcmp(direction_type, "triggerleft") == 0)
+                               {
+                                       qboolean new_left_trigger = 
abs(axis_value) > (32767 / 4);
+                                       if (new_left_trigger != left_trigger)
+                                       {
+                                               left_trigger = new_left_trigger;
+                                               Key_Event(K_TRIG_LEFT, 
left_trigger, true);
+                                       }
+                               }
+                               else if (strcmp(direction_type, "triggerright") 
== 0)
+                               {
+                                       qboolean new_right_trigger = 
abs(axis_value) > (32767 / 4);
+                                       if (new_right_trigger != right_trigger)
+                                       {
+                                               right_trigger = 
new_right_trigger;
+                                               Key_Event(K_TRIG_RIGHT, 
right_trigger, true);
+                                       }
+                               }
+                       }
+                               break;
+                       /* Joystick can have more buttons than on general game 
controller
+                        * so try to map not free buttons */
+                       case SDL_JOYBUTTONUP:
+                       case SDL_JOYBUTTONDOWN:
+                       {
+                               qboolean down = (event.type == 
SDL_JOYBUTTONDOWN);
+                               /* Ignore back button, we dont need event for 
such button */
+                               if (back_button_id == event.jbutton.button)
+                                       return;
+                               if(event.jbutton.button <= (K_JOY32 - K_JOY1)) {
+                                       Key_Event(event.jbutton.button + 
K_JOY1, down, true);
+                               }
+                       }
+                               break;
+                       case SDL_JOYHATMOTION:
+                       {
+                               if (last_hat != event.jhat.value)
+                               {
+                                       char diff = last_hat ^ event.jhat.value;
+                                       int i;
+                                       for (i=0; i < 4; i++) {
+                                               if (diff & (1 << i)) {
+                                                       /* check that we have 
button up for some bit */
+                                                       if (last_hat & (1 << i))
+                                                               Key_Event(i + 
K_HAT_UP, false, true);
 
+                                                       /* check that we have 
button down for some bit */
+                                                       if (event.jhat.value & 
(1 << i))
+                                                               Key_Event(i + 
K_HAT_UP, true, true);
+                                               }
+                                       }
+                                       last_hat = event.jhat.value;
+                               }
+                       }
+                               break;
+#endif
                        case SDL_QUIT:
                                Com_Quit();
-                               
+
                                break;
                }
        }
@@ -480,7 +708,7 @@ In_FlushQueue(void)
 
        Key_MarkAllUp();
 }
- 
+
 /*
  * Move handling
  */
@@ -559,8 +787,41 @@ IN_Move(usercmd_t *cmd)
 
                mouse_x = mouse_y = 0;
        }
+
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+       // to make the the viewangles changes independent of framerate
+       // we need to scale with frametime (assuming the configured values are 
for 60hz)
+       // 1/32768 is to normalize the input values from SDL (they're between 
-32768 and 32768 and we want -1 to 1)
+       // (for movement this is not needed, as those are absolute values 
independent of framerate)
+       float joyViewFactor = (1.0f/32768.0f) * (cls.rframetime/0.01666f);
+
+       if (joystick_yaw)
+       {
+               cl.viewangles[YAW] -= (m_yaw->value * joystick_yaw) * 
joyViewFactor;
+       }
+
+       if(joystick_pitch)
+       {
+               cl.viewangles[PITCH] += (m_pitch->value * joystick_pitch) * 
joyViewFactor;
+       }
+
+       if (joystick_forwardmove)
+       {
+               cmd->forwardmove -= (m_forward->value * joystick_forwardmove) / 
32768;
+       }
+
+       if (joystick_sidemove)
+       {
+               cmd->sidemove += (m_side->value * joystick_sidemove) / 32768;
+       }
+
+       if (joystick_up)
+       {
+               cmd->upmove -= (m_up->value * joystick_up) / 32768;
+       }
+#endif
 }
- 
+
 /* ------------------------------------------------------------------ */
 
 /*
@@ -582,7 +843,317 @@ IN_MLookUp(void)
        IN_CenterView();
 }
 
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+/*
+ * Shutdown haptic functionality
+ */
+static void IN_Haptic_Shutdown(void);
+
 /* ------------------------------------------------------------------ */
+/*
+ * Init haptic effects
+ */
+static int
+IN_Haptic_Effect_Init(int dir, int period, int magnitude, int length, int 
attack, int fade)
+{
+       /*
+        * Direction:
+        * North - 0
+        * East - 9000
+        * South - 18000
+        * West - 27000
+        */
+       int effect_id;
+       static SDL_HapticEffect haptic_effect;
+       SDL_memset(&haptic_effect, 0, sizeof(SDL_HapticEffect)); // 0 is safe 
default
+       haptic_effect.type = SDL_HAPTIC_SINE;
+       haptic_effect.periodic.direction.type = SDL_HAPTIC_POLAR; // Polar 
coordinates
+       haptic_effect.periodic.direction.dir[0] = dir;
+       haptic_effect.periodic.period = period;
+       haptic_effect.periodic.magnitude = magnitude;
+       haptic_effect.periodic.length = length;
+       haptic_effect.periodic.attack_length = attack;
+       haptic_effect.periodic.fade_length = fade;
+       effect_id = SDL_HapticNewEffect(joystick_haptic, &haptic_effect);
+       if (effect_id < 0)
+       {
+               Com_Printf ("SDL_HapticNewEffect failed: %s\n", SDL_GetError());
+               Com_Printf ("Please try to rerun game. Effects will be disabled 
for now.\n");
+               IN_Haptic_Shutdown();
+       }
+       return effect_id;
+}
+
+static int
+IN_Haptic_Effects_To_Id(int haptic_effect)
+{
+       if ((SDL_HapticQuery(joystick_haptic) & SDL_HAPTIC_SINE)==0)
+               return -1;
+
+       int hapric_volume = joy_haptic_magnitude->value * 255; // * 128 = 32767 
max strength;
+       if (hapric_volume > 255)
+               hapric_volume = 255;
+       else if (hapric_volume < 0)
+               hapric_volume = 0;
+
+       switch(haptic_effect) {
+       case HAPTIC_EFFECT_MENY:
+       case HAPTIC_EFFECT_TRAPCOCK:
+       case HAPTIC_EFFECT_STEP:
+               /* North */
+               return IN_Haptic_Effect_Init(
+                       0/* Force comes from N*/, 500/* 500 ms*/, hapric_volume 
* 48,
+                       200/* 0.2 seconds long */, 100/* Takes 0.1 second to 
get max strength */,
+                       100/* Takes 0.1 second to fade away */);
+       case HAPTIC_EFFECT_PAIN:
+               return IN_Haptic_Effect_Init(
+                       0/* Force comes from N*/, 700/* 700 ms*/, hapric_volume 
* 196,
+                       300/* 0.3 seconds long */, 200/* Takes 0.2 second to 
get max strength */,
+                       200/* Takes 0.2 second to fade away */);
+       case HAPTIC_EFFECT_BLASTER:
+               /* 30 degrees */
+               return IN_Haptic_Effect_Init(
+                       2000/* Force comes from NNE*/, 500/* 500 ms*/, 
hapric_volume * 64,
+                       200/* 0.2 seconds long */, 100/* Takes 0.1 second to 
get max strength */,
+                       100/* Takes 0.1 second to fade away */);
+       case HAPTIC_EFFECT_HYPER_BLASTER:
+               return IN_Haptic_Effect_Init(
+                       4000/* Force comes from NNE*/, 500/* 500 ms*/, 
hapric_volume * 64,
+                       200/* 0.2 seconds long */, 100/* Takes 0.1 second to 
get max strength */,
+                       100/* Takes 0.1 second to fade away */);
+       case HAPTIC_EFFECT_ETFRIFLE:
+               /* 60 degrees */
+               return IN_Haptic_Effect_Init(
+                       5000/* Force comes from NEE*/, 500/* 500 ms*/, 
hapric_volume * 64,
+                       200/* 0.2 seconds long */, 100/* Takes 0.1 second to 
get max strength */,
+                       100/* Takes 0.1 second to fade away */);
+       case HAPTIC_EFFECT_TRACKER:
+               return IN_Haptic_Effect_Init(
+                       7000/* Force comes from NEE*/, 500/* 500 ms*/, 
hapric_volume * 64,
+                       200/* 0.2 seconds long */, 100/* Takes 0.1 second to 
get max strength */,
+                       100/* Takes 0.1 second to fade away */);
+       case HAPTIC_EFFECT_MACHINEGUN:
+               /* 90 degrees */
+               return IN_Haptic_Effect_Init(
+                       9000/* Force comes from E*/, 800/* 800 ms*/, 
hapric_volume * 88,
+                       600/* 0.6 seconds long */, 200/* Takes 0.2 second to 
get max strength */,
+                       400/* Takes 0.4 second to fade away */);
+       case HAPTIC_EFFECT_SHOTGUN:
+               /* 120 degrees */
+               return IN_Haptic_Effect_Init(
+                       12000/* Force comes from EES*/, 700/* 700 ms*/, 
hapric_volume * 100,
+                       500/* 0.5 seconds long */, 100/* Takes 0.1 second to 
get max strength */,
+                       200/* Takes 0.2 second to fade away */);
+       case HAPTIC_EFFECT_SHOTGUN2:
+               /* 150 degrees */
+               return IN_Haptic_Effect_Init(
+                       14000/* Force comes from ESS*/, 700/* 700 ms*/, 
hapric_volume * 96,
+                       500/* 0.5 seconds long */, 100/* Takes 0.1 second to 
get max strength */,
+                       100/* Takes 0.1 second to fade away */);
+       case HAPTIC_EFFECT_SSHOTGUN:
+               return IN_Haptic_Effect_Init(
+                       16000/* Force comes from ESS*/, 700/* 700 ms*/, 
hapric_volume * 96,
+                       500/* 0.5 seconds long */, 100/* Takes 0.1 second to 
get max strength */,
+                       100/* Takes 0.1 second to fade away */);
+       case HAPTIC_EFFECT_RAILGUN:
+               /* 180 degrees */
+               return IN_Haptic_Effect_Init(
+                       18000/* Force comes from S*/, 700/* 700 ms*/, 
hapric_volume * 64,
+                       400/* 0.4 seconds long */, 100/* Takes 0.1 second to 
get max strength */,
+                       100/* Takes 0.1 second to fade away */);
+       case HAPTIC_EFFECT_ROCKETGUN:
+               /* 210 degrees */
+               return IN_Haptic_Effect_Init(
+                       21000/* Force comes from SSW*/, 700/* 700 ms*/, 
hapric_volume * 128,
+                       400/* 0.4 seconds long */, 300/* Takes 0.3 second to 
get max strength */,
+                       100/* Takes 0.1 second to fade away */);
+       case HAPTIC_EFFECT_GRENADE:
+               /* 240 degrees */
+               return IN_Haptic_Effect_Init(
+                       24000/* Force comes from SWW*/, 500/* 500 ms*/, 
hapric_volume * 64,
+                       200/* 0.2 seconds long */, 100/* Takes 0.1 second to 
get max strength */,
+                       100/* Takes 0.1 second to fade away */);
+       case HAPTIC_EFFECT_BFG:
+               /* 270 degrees */
+               return IN_Haptic_Effect_Init(
+                       27000/* Force comes from W*/, 800/* 800 ms*/, 
hapric_volume * 100,
+                       600/* 0.2 seconds long */, 100/* Takes 0.1 second to 
get max strength */,
+                       100/* Takes 0.1 second to fade away */);
+       case HAPTIC_EFFECT_PALANX:
+               /* 300 degrees */
+               return IN_Haptic_Effect_Init(
+                       30000/* Force comes from WWN*/, 500/* 500 ms*/, 
hapric_volume * 64,
+                       200/* 0.2 seconds long */, 100/* Takes 0.1 second to 
get max strength */,
+                       100/* Takes 0.1 second to fade away */);
+       case HAPTIC_EFFECT_IONRIPPER:
+               /* 330 degrees */
+               return IN_Haptic_Effect_Init(
+                       33000/* Force comes from WNN*/, 500/* 500 ms*/, 
hapric_volume * 64,
+                       200/* 0.2 seconds long */, 100/* Takes 0.1 second to 
get max strength */,
+                       100/* Takes 0.1 second to fade away */);
+       default:
+               return -1;
+       }
+}
+
+static void
+IN_Haptic_Effects_Info(void)
+{
+       show_haptic = true;
+       Com_Printf ("Joystic/Mouse haptic:\n");
+       Com_Printf (" * %d effects\n", SDL_HapticNumEffects(joystick_haptic));
+       Com_Printf (" * %d effects in same time\n", 
SDL_HapticNumEffectsPlaying(joystick_haptic));
+       Com_Printf (" * %d haptic axis\n", SDL_HapticNumAxes(joystick_haptic));
+}
+
+static void
+IN_Haptic_Effects_Init(void)
+{
+       last_haptic_efffect_size = SDL_HapticNumEffectsPlaying(joystick_haptic);
+       if (last_haptic_efffect_size > HAPTIC_EFFECT_LAST)
+               last_haptic_efffect_size = HAPTIC_EFFECT_LAST;
+       for (int i=0; i<HAPTIC_EFFECT_LAST; i++)
+       {
+               last_haptic_efffect[i].effect_type = HAPTIC_EFFECT_UNKNOWN;
+               last_haptic_efffect[i].effect_id = -1;
+       }
+}
+
+/*
+ * Shuts the backend down
+ */
+static void
+IN_Haptic_Effect_Shutdown(int * effect_id)
+{
+       if (!effect_id)
+               return;
+       if (*effect_id >= 0)
+               SDL_HapticDestroyEffect(joystick_haptic, *effect_id);
+       *effect_id = -1;
+}
+
+static void
+IN_Haptic_Effects_Shutdown(void)
+{
+       for (int i=0; i<HAPTIC_EFFECT_LAST; i++)
+       {
+               last_haptic_efffect[i].effect_type = HAPTIC_EFFECT_UNKNOWN;
+               IN_Haptic_Effect_Shutdown(&last_haptic_efffect[i].effect_id);
+       }
+}
+#endif
+
+void
+Haptic_Feedback(char *name)
+{
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+       int effect_type = HAPTIC_EFFECT_UNKNOWN;
+
+       if (joy_haptic_magnitude->value <= 0)
+               return;
+
+       if (!joystick_haptic)
+               return;
+
+       if (last_haptic_volume != (int)(joy_haptic_magnitude->value * 255))
+       {
+               IN_Haptic_Effects_Shutdown();
+               IN_Haptic_Effects_Init();
+       }
+       last_haptic_volume = joy_haptic_magnitude->value * 255;
+
+       if (strstr(name, "misc/menu"))
+       {
+               effect_type = HAPTIC_EFFECT_MENY;
+       }
+       else if (strstr(name, "weapons/blastf1a"))
+       {
+               effect_type = HAPTIC_EFFECT_BLASTER;
+       }
+       else if (strstr(name, "weapons/hyprbf1a"))
+       {
+               effect_type = HAPTIC_EFFECT_HYPER_BLASTER;
+       }
+       else if (strstr(name, "weapons/machgf"))
+       {
+               effect_type = HAPTIC_EFFECT_MACHINEGUN;
+       }
+       else if (strstr(name, "weapons/shotgf1b"))
+       {
+               effect_type = HAPTIC_EFFECT_SHOTGUN;
+       }
+       else if (strstr(name, "weapons/sshotf1b"))
+       {
+               effect_type = HAPTIC_EFFECT_SSHOTGUN;
+       }
+       else if (strstr(name, "weapons/railgf1a"))
+       {
+               effect_type = HAPTIC_EFFECT_RAILGUN;
+       }
+       else if (strstr(name, "weapons/rocklf1a"))
+       {
+               effect_type = HAPTIC_EFFECT_ROCKETGUN;
+       }
+       else if (strstr(name, "weapons/grenlf1a") || strstr(name, 
"weapons/hgrent1a"))
+       {
+               effect_type = HAPTIC_EFFECT_GRENADE;
+       }
+       else if (strstr(name, "weapons/bfg__f1y"))
+       {
+               effect_type = HAPTIC_EFFECT_BFG;
+       }
+       else if (strstr(name, "weapons/plasshot"))
+       {
+               effect_type = HAPTIC_EFFECT_PALANX;
+       }
+       else if (strstr(name, "weapons/rippfire"))
+       {
+               effect_type = HAPTIC_EFFECT_IONRIPPER;
+       }
+       else if (strstr(name, "weapons/nail1"))
+       {
+               effect_type = HAPTIC_EFFECT_ETFRIFLE;
+       }
+       else if (strstr(name, "weapons/shotg2"))
+       {
+               effect_type = HAPTIC_EFFECT_SHOTGUN2;
+       }
+       else if (strstr(name, "weapons/disint2"))
+       {
+               effect_type = HAPTIC_EFFECT_TRACKER;
+       }
+       else if (strstr(name, "player/male/pain") ||
+               strstr(name, "player/female/pain") ||
+               strstr(name, "players/male/pain") ||
+               strstr(name, "players/female/pain"))
+       {
+               effect_type = HAPTIC_EFFECT_PAIN;
+       }
+       else if (strstr(name, "player/step") ||
+               strstr(name, "player/land"))
+       {
+               effect_type = HAPTIC_EFFECT_STEP;
+       }
+       else if (strstr(name, "weapons/trapcock"))
+       {
+               effect_type = HAPTIC_EFFECT_TRAPCOCK;
+       }
+
+       if (effect_type != HAPTIC_EFFECT_UNKNOWN)
+       {
+               // check last effect for reuse
+               if (last_haptic_efffect[last_haptic_efffect_pos].effect_type != 
effect_type)
+               {
+                       // FIFO for effects
+                       last_haptic_efffect_pos = (last_haptic_efffect_pos+1) % 
last_haptic_efffect_size;
+                       
IN_Haptic_Effect_Shutdown(&last_haptic_efffect[last_haptic_efffect_pos].effect_id);
+                       
last_haptic_efffect[last_haptic_efffect_pos].effect_type = effect_type;
+                       last_haptic_efffect[last_haptic_efffect_pos].effect_id 
= IN_Haptic_Effects_To_Id(effect_type);
+               }
+               SDL_HapticRunEffect(joystick_haptic, 
last_haptic_efffect[last_haptic_efffect_pos].effect_id, 1);
+       }
+#endif
+}
 
 /*
  * Initializes the backend
@@ -594,17 +1165,46 @@ IN_Init(void)
 
        mouse_x = mouse_y = 0;
 
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+       joystick_yaw = joystick_pitch = joystick_forwardmove = 
joystick_sidemove = 0;
+#endif
+
        exponential_speedup = Cvar_Get("exponential_speedup", "0", 
CVAR_ARCHIVE);
        freelook = Cvar_Get("freelook", "1", 0);
        in_grab = Cvar_Get("in_grab", "2", CVAR_ARCHIVE);
-       in_mouse = Cvar_Get("in_mouse", "0", CVAR_ARCHIVE);
        lookstrafe = Cvar_Get("lookstrafe", "0", 0);
        m_filter = Cvar_Get("m_filter", "0", CVAR_ARCHIVE);
+       m_up = Cvar_Get("m_up", "1", 0);
        m_forward = Cvar_Get("m_forward", "1", 0);
        m_pitch = Cvar_Get("m_pitch", "0.022", 0);
        m_side = Cvar_Get("m_side", "0.8", 0);
        m_yaw = Cvar_Get("m_yaw", "0.022", 0);
        sensitivity = Cvar_Get("sensitivity", "3", 0);
+
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+       joy_haptic_magnitude = Cvar_Get("joy_haptic_magnitude", "0.0", 
CVAR_ARCHIVE);
+
+       joy_yawsensitivity = Cvar_Get("joy_yawsensitivity", "1.0", 
CVAR_ARCHIVE);
+       joy_pitchsensitivity = Cvar_Get("joy_pitchsensitivity", "1.0", 
CVAR_ARCHIVE);
+       joy_forwardsensitivity = Cvar_Get("joy_forwardsensitivity", "1.0", 
CVAR_ARCHIVE);
+       joy_sidesensitivity = Cvar_Get("joy_sidesensitivity", "1.0", 
CVAR_ARCHIVE);
+       joy_upsensitivity = Cvar_Get("joy_upsensitivity", "1.0", CVAR_ARCHIVE);
+
+       joy_axis_leftx = Cvar_Get("joy_axis_leftx", "sidemove", CVAR_ARCHIVE);
+       joy_axis_lefty = Cvar_Get("joy_axis_lefty", "forwardmove", 
CVAR_ARCHIVE);
+       joy_axis_rightx = Cvar_Get("joy_axis_rightx", "yaw", CVAR_ARCHIVE);
+       joy_axis_righty = Cvar_Get("joy_axis_righty", "pitch", CVAR_ARCHIVE);
+       joy_axis_triggerleft = Cvar_Get("joy_axis_triggerleft", "triggerleft", 
CVAR_ARCHIVE);
+       joy_axis_triggerright = Cvar_Get("joy_axis_triggerright", 
"triggerright", CVAR_ARCHIVE);
+
+       joy_axis_leftx_threshold = Cvar_Get("joy_axis_leftx_threshold", "0.15", 
CVAR_ARCHIVE);
+       joy_axis_lefty_threshold = Cvar_Get("joy_axis_lefty_threshold", "0.15", 
CVAR_ARCHIVE);
+       joy_axis_rightx_threshold = Cvar_Get("joy_axis_rightx_threshold", 
"0.15", CVAR_ARCHIVE);
+       joy_axis_righty_threshold = Cvar_Get("joy_axis_righty_threshold", 
"0.15", CVAR_ARCHIVE);
+       joy_axis_triggerleft_threshold = 
Cvar_Get("joy_axis_triggerleft_threshold", "0.15", CVAR_ARCHIVE);
+       joy_axis_triggerright_threshold = 
Cvar_Get("joy_axis_triggerright_threshold", "0.15", CVAR_ARCHIVE);
+#endif
+
        vid_fullscreen = Cvar_Get("vid_fullscreen", "0", CVAR_ARCHIVE);
        windowed_mouse = Cvar_Get("windowed_mouse", "1", CVAR_USERINFO | 
CVAR_ARCHIVE);
 
@@ -617,12 +1217,103 @@ IN_Init(void)
        SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, 
SDL_DEFAULT_REPEAT_INTERVAL);
 #endif
 
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+       /* joystik init */
+       if (!SDL_WasInit(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC))
+       {
+               if (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) == -1)
+               {
+                       Com_Printf ("Couldn't init SDL joystick: %s.\n", 
SDL_GetError ());
+               } else {
+                       Com_Printf ("%i joysticks were found.\n", 
SDL_NumJoysticks());
+                       if (SDL_NumJoysticks() > 0) {
+                               int i;
+                               for (i=0; i<SDL_NumJoysticks(); i ++) {
+                                       joystick = SDL_JoystickOpen(i);
+                                       Com_Printf ("The name of the joystick 
is '%s'\n", SDL_JoystickName(joystick));
+                                       Com_Printf ("Number of Axes: %d\n", 
SDL_JoystickNumAxes(joystick));
+                                       Com_Printf ("Number of Buttons: %d\n", 
SDL_JoystickNumButtons(joystick));
+                                       Com_Printf ("Number of Balls: %d\n", 
SDL_JoystickNumBalls(joystick));
+                                       Com_Printf ("Number of Hats: %d\n", 
SDL_JoystickNumHats(joystick));
+
+                                       joystick_haptic = 
SDL_HapticOpenFromJoystick(joystick);
+                                       if (joystick_haptic == NULL)
+                                               Com_Printf ("Most likely 
joystick isn't haptic\n");
+                                       else
+                                               IN_Haptic_Effects_Info();
+
+                                       if(SDL_IsGameController(i))
+                                       {
+                                               SDL_GameControllerButtonBind 
backBind;
+                                               controller = 
SDL_GameControllerOpen(i);
+                                               Com_Printf ("Controller 
settings: %s\n", SDL_GameControllerMapping(controller));
+                                               Com_Printf ("Controller axis: 
\n");
+                                               Com_Printf (" * leftx = %s\n", 
joy_axis_leftx->string);
+                                               Com_Printf (" * lefty = %s\n", 
joy_axis_lefty->string);
+                                               Com_Printf (" * rightx = %s\n", 
joy_axis_rightx->string);
+                                               Com_Printf (" * righty = %s\n", 
joy_axis_righty->string);
+                                               Com_Printf (" * triggerleft = 
%s\n", joy_axis_triggerleft->string);
+                                               Com_Printf (" * triggerright = 
%s\n", joy_axis_triggerright->string);
+
+                                               Com_Printf ("Controller 
thresholds: \n");
+                                               Com_Printf (" * leftx = %f\n", 
joy_axis_leftx_threshold->value);
+                                               Com_Printf (" * lefty = %f\n", 
joy_axis_lefty_threshold->value);
+                                               Com_Printf (" * rightx = %f\n", 
joy_axis_rightx_threshold->value);
+                                               Com_Printf (" * righty = %f\n", 
joy_axis_righty_threshold->value);
+                                               Com_Printf (" * triggerleft = 
%f\n", joy_axis_triggerleft_threshold->value);
+                                               Com_Printf (" * triggerright = 
%f\n", joy_axis_triggerright_threshold->value);
+
+                                               backBind = 
SDL_GameControllerGetBindForButton(controller, SDL_CONTROLLER_BUTTON_BACK);
+
+                                               if (backBind.bindType == 
SDL_CONTROLLER_BINDTYPE_BUTTON) {
+                                                       back_button_id = 
backBind.value.button;
+                                                       Com_Printf ("\nBack 
button JOY%d will be unbindable.\n", back_button_id+1);
+                                               }
+                                               break;
+                                       }
+                                       else
+                                       {
+                                               char joystick_guid[256] = {0};
+                                               SDL_JoystickGUID guid;
+                                               guid = 
SDL_JoystickGetDeviceGUID(i);
+                                               SDL_JoystickGetGUIDString(guid, 
joystick_guid, 255);
+                                               Com_Printf ("For use joystic as 
game contoller please set SDL_GAMECONTROLLERCONFIG:\n");
+                                               Com_Printf ("e.g.: 
SDL_GAMECONTROLLERCONFIG='%s,%s,leftx:a0,lefty:a1,rightx:a2,righty:a3,back:b1,...\n",
 joystick_guid, SDL_JoystickName(joystick));
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               joystick_haptic = SDL_HapticOpenFromMouse();
+                               if (joystick_haptic == NULL)
+                                       Com_Printf ("Most likely mouse isn't 
haptic\n");
+                               else
+                                       IN_Haptic_Effects_Info();
+                       }
+               }
+       }
+#endif
+
        Com_Printf("------------------------------------\n\n");
 }
 
+#if SDL_VERSION_ATLEAST(2, 0, 0)
 /*
  * Shuts the backend down
  */
+static void
+IN_Haptic_Shutdown(void)
+{
+       if (joystick_haptic)
+       {
+               IN_Haptic_Effects_Shutdown();
+
+               SDL_HapticClose(joystick_haptic);
+               joystick_haptic = NULL;
+       }
+}
+#endif
+
 void
 IN_Shutdown(void)
 {
@@ -630,7 +1321,24 @@ IN_Shutdown(void)
        Cmd_RemoveCommand("+mlook");
        Cmd_RemoveCommand("-mlook");
 
-    Com_Printf("Shutting down input.\n");
+       Com_Printf("Shutting down input.\n");
+
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+       IN_Haptic_Shutdown();
+
+       if (controller)
+       {
+               back_button_id = -1;
+               SDL_GameControllerClose(controller);
+               controller  = NULL;
+       }
+
+       if (joystick)
+       {
+               SDL_JoystickClose(joystick);
+               joystick = NULL;
+       }
+#endif
 }
 
 /* ------------------------------------------------------------------ */
diff --git a/src/backends/sdl/refresh.c b/src/backends/sdl/refresh.c
index 073db64..569bce7 100644
--- a/src/backends/sdl/refresh.c
+++ b/src/backends/sdl/refresh.c
@@ -232,6 +232,7 @@ static qboolean GetWindowSize(int* w, int* h)
        return true;
 }
 
+static qboolean initSuccessful = false;
 
 /*
  * Initializes the OpenGL window
@@ -257,7 +258,9 @@ GLimp_InitGraphics(int fullscreen, int *pwidth, int 
*pheight)
        }
 #endif
 
-       if (GetWindowSize(&curWidth, &curHeight) && (curWidth == width) && 
(curHeight == height))
+       // only do this if we already have a working window and fully 
initialized rendering backend
+       // (GLimp_InitGraphics() is also called when recovering if creating GL 
context fails or the one we got is unusable)
+       if (initSuccessful && GetWindowSize(&curWidth, &curHeight) && (curWidth 
== width) && (curHeight == height))
        {
                /* If we want fullscreen, but aren't */
                if (fullscreen != IsFullscreen())
@@ -319,20 +322,17 @@ GLimp_InitGraphics(int fullscreen, int *pwidth, int 
*pheight)
        {
                if (!CreateSDLWindow(flags, width, height))
                {
-                       if (flags & SDL_OPENGL)
+                       if((flags & SDL_OPENGL) && gl_msaa_samples->value)
                        {
-                               if (gl_msaa_samples->value)
-                               {
-                                       Com_Printf("SDL SetVideoMode failed: 
%s\n", SDL_GetError());
-                                       Com_Printf("Reverting to %s gl_mode %i 
(%ix%i) without MSAA.\n",
-                                                  (flags & fs_flag) ? 
"fullscreen" : "windowed",
-                                                  (int) 
Cvar_VariableValue("gl_mode"), width, height);
-
-                                       /* Try to recover */
-                                       Cvar_SetValue("gl_msaa_samples", 0);
-                                       
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
-                                       
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
-                               }
+                               Com_Printf("SDL SetVideoMode failed: %s\n", 
SDL_GetError());
+                               Com_Printf("Reverting to %s gl_mode %i (%ix%i) 
without MSAA.\n",
+                                               (flags & fs_flag) ? 
"fullscreen" : "windowed",
+                                               (int) 
Cvar_VariableValue("gl_mode"), width, height);
+
+                               /* Try to recover */
+                               Cvar_SetValue("gl_msaa_samples", 0);
+                               SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 
0);
+                               SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 
0);
                        }
                        else if (width != 640 || height != 480 || (flags & 
fs_flag))
                        {
@@ -374,6 +374,8 @@ GLimp_InitGraphics(int fullscreen, int *pwidth, int 
*pheight)
        /* No cursor */
        SDL_ShowCursor(0);
 
+       initSuccessful = true;
+
        return true;
 }
 
@@ -453,6 +455,8 @@ VID_ShutdownWindow(void)
        // make sure that after vid_restart the refreshrate will be queried 
from SDL2 again.
        glimp_refreshRate = -1;
 
+       initSuccessful = false; // not initialized anymore
+
        if (SDL_WasInit(SDL_INIT_EVERYTHING) == SDL_INIT_VIDEO)
        {
                SDL_Quit();
diff --git a/src/backends/unix/system.c b/src/backends/unix/system.c
index 410f436..1b6409a 100644
--- a/src/backends/unix/system.c
+++ b/src/backends/unix/system.c
@@ -46,6 +46,11 @@
 #include <dirent.h>
 #include <time.h>
 
+#ifdef __APPLE__
+#include <mach/clock.h>
+#include <mach/mach.h>
+#endif
+
 #include "../../common/header/common.h"
 #include "../../common/header/glob.h"
 #include "../generic/header/input.h"
@@ -82,19 +87,49 @@ Sys_Init(void)
 long long
 Sys_Microseconds(void)
 {
-       static struct timespec last;
-       struct timespec now;
+#ifdef __APPLE__
+       // OSX didn't have clock_gettime() until recently, so use Mach's 
clock_get_time()
+       // instead. fortunately its mach_timespec_t seems identical to POSIX 
struct timespec
+       // so lots of code can be shared
+       clock_serv_t cclock;
+       mach_timespec_t now;
+       static mach_timespec_t first;
+
+       host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
+       clock_get_time(cclock, &now);
+       mach_port_deallocate(mach_task_self(), cclock);
+
+#else // not __APPLE__ - other Unix-likes will hopefully support 
clock_gettime()
 
+       struct timespec now;
+       static struct timespec first;
+  #ifdef _POSIX_MONOTONIC_CLOCK
        clock_gettime(CLOCK_MONOTONIC, &now);
+  #else
+       clock_gettime(CLOCK_REALTIME, &now);
+  #endif
+
+#endif // not __APPLE__
 
-       if(last.tv_sec == 0)
+       if(first.tv_sec == 0)
        {
-               clock_gettime(CLOCK_MONOTONIC, &last);
-               return last.tv_nsec / 1000ll;
+               long long nsec = now.tv_nsec;
+               long long sec = now.tv_sec;
+               // set back first by 1ms so neither this function nor 
Sys_Milliseconds()
+               // (which calls this) will ever return 0
+               nsec -= 1000000; 
+               if(nsec < 0)
+               {
+                       nsec += 1000000000ll; // 1s in ns => definitely 
positive now
+                       --sec;
+               }
+
+               first.tv_sec = sec;
+               first.tv_nsec = nsec;
        }
 
-       long long sec = now.tv_sec - last.tv_sec;
-       long long nsec = now.tv_nsec - last.tv_nsec;
+       long long sec = now.tv_sec - first.tv_sec;
+       long long nsec = now.tv_nsec - first.tv_nsec;
 
        if(nsec < 0)
        {
diff --git a/src/backends/windows/system.c b/src/backends/windows/system.c
index 94c4267..3fa5147 100644
--- a/src/backends/windows/system.c
+++ b/src/backends/windows/system.c
@@ -432,7 +432,7 @@ Sys_Microseconds(void)
 
        if (!uSecbase)
        {
-               uSecbase = microseconds / 1000ll;
+               uSecbase = microseconds - 1001ll;
        }
 
        return microseconds - uSecbase;
diff --git a/src/client/cl_cin.c b/src/client/cl_cin.c
index 36b278b..62644e4 100644
--- a/src/client/cl_cin.c
+++ b/src/client/cl_cin.c
@@ -66,7 +66,7 @@ SCR_LoadPCX(char *filename, byte **pic, byte **palette, int 
*width, int *height)
        byte *raw;
        pcx_t *pcx;
        int x, y;
-       int len;
+       int len, full_size;
        int dataByte, runLength;
        byte *out, *pix;
 
@@ -75,7 +75,7 @@ SCR_LoadPCX(char *filename, byte **pic, byte **palette, int 
*width, int *height)
        /* load the file */
        len = FS_LoadFile(filename, (void **)&raw);
 
-       if (!raw)
+       if (!raw || len < sizeof(pcx_t))
        {
                return;
        }
@@ -95,7 +95,8 @@ SCR_LoadPCX(char *filename, byte **pic, byte **palette, int 
*width, int *height)
                return;
        }
 
-       out = Z_Malloc((pcx->ymax + 1) * (pcx->xmax + 1));
+       full_size = (pcx->ymax + 1) * (pcx->xmax + 1);
+       out = Z_Malloc(full_size);
 
        *pic = out;
 
@@ -135,7 +136,15 @@ SCR_LoadPCX(char *filename, byte **pic, byte **palette, 
int *width, int *height)
 
                        while (runLength-- > 0)
                        {
-                               pix[x++] = dataByte;
+                               if ((*pic + full_size) <= (pix + x))
+                               {
+                                       x += runLength;
+                                       runLength = 0;
+                               }
+                               else
+                               {
+                                       pix[x++] = dataByte;
+                               }
                        }
                }
        }
diff --git a/src/client/cl_keyboard.c b/src/client/cl_keyboard.c
index 2b3c26c..3e612b3 100644
--- a/src/client/cl_keyboard.c
+++ b/src/client/cl_keyboard.c
@@ -106,6 +106,49 @@ keyname_t keynames[] = {
        {"MOUSE4", K_MOUSE4},
        {"MOUSE5", K_MOUSE5},
 
+       {"JOY1", K_JOY1},
+       {"JOY2", K_JOY2},
+       {"JOY3", K_JOY3},
+       {"JOY4", K_JOY4},
+       {"JOY5", K_JOY5},
+       {"JOY6", K_JOY6},
+       {"JOY7", K_JOY7},
+       {"JOY8", K_JOY8},
+       {"JOY9", K_JOY9},
+       {"JOY10", K_JOY10},
+       {"JOY11", K_JOY11},
+       {"JOY12", K_JOY12},
+       {"JOY13", K_JOY13},
+       {"JOY14", K_JOY14},
+       {"JOY15", K_JOY15},
+       {"JOY16", K_JOY16},
+       {"JOY17", K_JOY17},
+       {"JOY18", K_JOY18},
+       {"JOY19", K_JOY19},
+       {"JOY20", K_JOY20},
+       {"JOY21", K_JOY21},
+       {"JOY22", K_JOY22},
+       {"JOY23", K_JOY23},
+       {"JOY24", K_JOY24},
+       {"JOY25", K_JOY25},
+       {"JOY26", K_JOY26},
+       {"JOY27", K_JOY27},
+       {"JOY28", K_JOY28},
+       {"JOY29", K_JOY29},
+       {"JOY30", K_JOY30},
+       {"JOY31", K_JOY31},
+       {"JOY32", K_JOY32},
+
+       {"HAT_UP", K_HAT_UP},
+       {"HAT_RIGHT", K_HAT_RIGHT},
+       {"HAT_DOWN", K_HAT_DOWN},
+       {"HAT_LEFT", K_HAT_LEFT},
+
+       {"TRIG_LEFT", K_TRIG_LEFT},
+       {"TRIG_RIGHT", K_TRIG_RIGHT},
+
+       {"JOY_BACK", K_JOY_BACK},
+
        {"AUX1", K_AUX1},
        {"AUX2", K_AUX2},
        {"AUX3", K_AUX3},
@@ -562,7 +605,7 @@ Key_Message(int key)
 }
 
 /*
- * Returns a key number to be used to index 
+ * Returns a key number to be used to index
  * keybindings[] by looking at the given string.
  * Single ascii characters return themselves, while
  * the K_* names are matched up.
@@ -715,7 +758,7 @@ Key_Bind_f(void)
        }
 
        /* don't allow binding escape or the special console keys */
-       if(b == K_ESCAPE || b == '^' || b == '`' || b == '~')
+       if(b == K_ESCAPE || b == '^' || b == '`' || b == '~' || b == K_JOY_BACK)
        {
                if(doneWithDefaultCfg)
                {
@@ -773,7 +816,7 @@ Key_WriteBindings(FILE *f)
        {
                if (keybindings[i] && keybindings[i][0])
                {
-                       fprintf(f, "bind %s \"%s\"\n", 
+                       fprintf(f, "bind %s \"%s\"\n",
                                        Key_KeynumToString(i), keybindings[i]);
                }
        }
@@ -1061,12 +1104,12 @@ Key_Event(int key, qboolean down, qboolean special)
        }
 
        /* Key is unbound */
-       if ((key >= 200) && !keybindings[key] && (cls.key_dest != key_console))
+       if ((key >= K_MOUSE1 && key != K_JOY_BACK) && !keybindings[key] && 
(cls.key_dest != key_console))
        {
                Com_Printf("%s is unbound, hit F4 to set.\n", 
Key_KeynumToString(key));
        }
 
-    /* While in attract loop all keys besides F1 to F12 (to
+       /* While in attract loop all keys besides F1 to F12 (to
           allow quick load and the like) are treated like escape. */
        if (cl.attractloop && (cls.key_dest != key_menu) &&
                !((key >= K_F1) && (key <= K_F12)))
@@ -1081,10 +1124,11 @@ Key_Event(int key, qboolean down, qboolean special)
           - moves one menu level up
           - closes the menu
           - closes the help computer
-          - closes the chat window */
+          - closes the chat window
+          Fully same logic for K_JOY_BACK */
        if (!cls.disable_screen)
        {
-               if (key == K_ESCAPE)
+               if (key == K_ESCAPE || key == K_JOY_BACK)
                {
                        if (!down)
                        {
diff --git a/src/client/cl_main.c b/src/client/cl_main.c
index 3ee4091..4180206 100644
--- a/src/client/cl_main.c
+++ b/src/client/cl_main.c
@@ -57,7 +57,6 @@ cvar_t *cl_showclamp;
 
 cvar_t *cl_paused;
 
-cvar_t *lookspring;
 cvar_t *lookstrafe;
 cvar_t *sensitivity;
 
@@ -93,6 +92,11 @@ centity_t cl_entities[MAX_EDICTS];
 
 entity_state_t cl_parse_entities[MAX_PARSE_ENTITIES];
 
+/*Evil hack against too many power screen and power
+  shield impact sounds. For example if the player
+  fires his shotgun onto a Brain. */
+int num_power_sounds;
+
 extern cvar_t *allow_download;
 extern cvar_t *allow_download_players;
 extern cvar_t *allow_download_models;
@@ -490,7 +494,6 @@ CL_InitLocal(void)
 
        cl_run = Cvar_Get("cl_run", "0", CVAR_ARCHIVE);
        freelook = Cvar_Get("freelook", "1", CVAR_ARCHIVE);
-       lookspring = Cvar_Get("lookspring", "0", CVAR_ARCHIVE);
        lookstrafe = Cvar_Get("lookstrafe", "0", CVAR_ARCHIVE);
        sensitivity = Cvar_Get("sensitivity", "3", CVAR_ARCHIVE);
 
@@ -735,6 +738,9 @@ CL_Frame(int packetdelta, int renderdelta, int timedelta, 
qboolean packetframe,
                cls.netchan.last_received = Sys_Milliseconds();
        }
 
+       // Reset power shield / power screen sound counter.
+       num_power_sounds = 0;
+
        if (!cl_timedemo->value)
        {
                // Don't throttle too much when connecting / loading.
diff --git a/src/client/cl_prediction.c b/src/client/cl_prediction.c
index a451e23..b607f9e 100644
--- a/src/client/cl_prediction.c
+++ b/src/client/cl_prediction.c
@@ -231,6 +231,7 @@ CL_PredictMovement(void)
        pmove_t pm;
        int i;
        int step;
+       vec3_t tmp;
 
        if (cls.state != ca_active)
        {
@@ -296,9 +297,10 @@ CL_PredictMovement(void)
        }
 
        step = pm.s.origin[2] - (int)(cl.predicted_origin[2] * 8);
+       VectorCopy(tmp, pm.s.velocity);
 
        if (((step > 126 && step < 130))
-               && !VectorCompare((float *)pm.s.velocity, vec3_origin)
+               && !VectorCompare(tmp, vec3_origin)
                && (pm.s.pm_flags & PMF_ON_GROUND))
        {
                cl.predicted_step = step * 0.125f;
diff --git a/src/client/cl_screen.c b/src/client/cl_screen.c
index e7a6f9d..1c0334d 100644
--- a/src/client/cl_screen.c
+++ b/src/client/cl_screen.c
@@ -1445,7 +1445,7 @@ SCR_Framecounter(void) {
 
                char str[10];
                snprintf(str, sizeof(str), "%3.2ffps", (1000.0 * 1000.0) / (avg 
/ num));
-               DrawStringScaled(scale*(viddef.width - 80), 0, str, scale);
+               DrawStringScaled(viddef.width - scale*(strlen(str)*8 + 2), 0, 
str, scale);
        } else if (cl_drawfps->value >= 2) {
                // Calculate average of frames.
                int avg = 0;
diff --git a/src/client/cl_tempentities.c b/src/client/cl_tempentities.c
index 0cb4f69..5b3f539 100644
--- a/src/client/cl_tempentities.c
+++ b/src/client/cl_tempentities.c
@@ -24,7 +24,9 @@
  * =======================================================================
  */
 
+#include <SDL2/SDL_scancode.h>
 #include "header/client.h"
+#include "sound/header/local.h"
 
 typedef enum
 {
@@ -716,7 +718,23 @@ CL_ParseTEnt(void)
                                CL_ParticleEffect(pos, dir, 0xb0, 40);
                        }
 
-                       S_StartSound(pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
+                       num_power_sounds++;
+
+                       /* If too many of these sounds are started in one frame 
(for
+                        * example if the player shoots with the super shotgun 
into
+                        * the power screen of a Brain) things get too loud and 
OpenAL
+                        * is forced to scale the volume of several other 
sounds and
+                        * the background music down. That leads to a noticable 
and
+                        * annoying drop in the overall volume.
+                        *
+                        * Work around that by limiting the number of sounds 
started.
+                        * 16 was choosen by empirical testing.
+                        */
+                       if (sound_started == SS_OAL && num_power_sounds < 16)
+                       {
+                               S_StartSound(pos, 0, 0, cl_sfx_lashit, 1, 
ATTN_NORM, 0);
+                       }
+
                        break;
 
                case TE_SHOTGUN: /* bullet hitting wall */
diff --git a/src/client/header/client.h b/src/client/header/client.h
index eacad82..9c58b0e 100644
--- a/src/client/header/client.h
+++ b/src/client/header/client.h
@@ -249,6 +249,11 @@ typedef struct
 
 extern client_static_t cls;
 
+/*Evil hack against too many power screen and power
+  shield impact sounds. For example if the player
+  fires his shotgun onto a Brain. */
+extern int num_power_sounds;
+
 /* cvars */
 extern cvar_t  *gl_stereo_separation;
 extern cvar_t  *gl_stereo_convergence;
@@ -271,7 +276,6 @@ extern      cvar_t  *cl_anglespeedkey;
 extern cvar_t  *cl_shownet;
 extern cvar_t  *cl_showmiss;
 extern cvar_t  *cl_showclamp;
-extern cvar_t  *lookspring;
 extern cvar_t  *lookstrafe;
 extern cvar_t  *sensitivity;
 extern cvar_t  *m_pitch;
diff --git a/src/client/header/keyboard.h b/src/client/header/keyboard.h
index c6431c0..668606b 100644
--- a/src/client/header/keyboard.h
+++ b/src/client/header/keyboard.h
@@ -144,6 +144,17 @@ enum QKEYS {
        K_JOY31,
        K_JOY32,
 
+       K_HAT_UP,
+       K_HAT_RIGHT,
+       K_HAT_DOWN,
+       K_HAT_LEFT,
+
+       K_TRIG_LEFT,
+       K_TRIG_RIGHT,
+
+       /* Can't be mapped to any action */
+       K_JOY_BACK,
+
        K_AUX1,
        K_AUX2,
        K_AUX3,
@@ -305,6 +316,7 @@ void Key_ReadConsoleHistory();
 void Key_WriteConsoleHistory();
 void Key_SetBinding(int keynum, char *binding);
 void Key_MarkAllUp(void);
-int Key_GetKey(void);
+void Haptic_Feedback(char *name);
+int Key_GetMenuKey(int key);
 
 #endif
diff --git a/src/client/menu/menu.c b/src/client/menu/menu.c
index b8c07f6..93336f1 100644
--- a/src/client/menu/menu.c
+++ b/src/client/menu/menu.c
@@ -165,7 +165,7 @@ M_PushMenu(void (*draw)(void), const char *(*key)(int))
     }
 
 #ifdef USE_OPENAL
-    if (cl.cinematic_file)
+    if (cl.cinematic_file && sound_started == SS_OAL)
     {
         AL_UnqueueRawSamples();
     }
@@ -215,11 +215,122 @@ M_PushMenu(void (*draw)(void), const char *(*key)(int))
     cls.key_dest = key_menu;
 }
 
+int
+Key_GetMenuKey(int key)
+{
+       switch (key)
+       {
+               case K_KP_UPARROW:
+               case K_UPARROW:
+               case K_HAT_UP:
+                       return K_UPARROW;
+
+               case K_TAB:
+               case K_KP_DOWNARROW:
+               case K_DOWNARROW:
+               case K_HAT_DOWN:
+                       return K_DOWNARROW;
+
+               case K_KP_LEFTARROW:
+               case K_LEFTARROW:
+               case K_HAT_LEFT:
+               case K_TRIG_LEFT:
+                       return K_LEFTARROW;
+
+               case K_KP_RIGHTARROW:
+               case K_RIGHTARROW:
+               case K_HAT_RIGHT:
+               case K_TRIG_RIGHT:
+                       return K_RIGHTARROW;
+
+               case K_MOUSE1:
+               case K_MOUSE2:
+               case K_MOUSE3:
+               case K_MOUSE4:
+               case K_MOUSE5:
+
+               case K_JOY1:
+               case K_JOY2:
+               case K_JOY3:
+               case K_JOY4:
+               case K_JOY5:
+               case K_JOY6:
+               case K_JOY7:
+               case K_JOY8:
+               case K_JOY9:
+               case K_JOY10:
+               case K_JOY11:
+               case K_JOY12:
+               case K_JOY13:
+               case K_JOY14:
+               case K_JOY15:
+               case K_JOY16:
+               case K_JOY17:
+               case K_JOY18:
+               case K_JOY19:
+               case K_JOY20:
+               case K_JOY21:
+               case K_JOY22:
+               case K_JOY23:
+               case K_JOY24:
+               case K_JOY25:
+               case K_JOY26:
+               case K_JOY27:
+               case K_JOY28:
+               case K_JOY29:
+               case K_JOY30:
+               case K_JOY31:
+
+               case K_AUX1:
+               case K_AUX2:
+               case K_AUX3:
+               case K_AUX4:
+               case K_AUX5:
+               case K_AUX6:
+               case K_AUX7:
+               case K_AUX8:
+               case K_AUX9:
+               case K_AUX10:
+               case K_AUX11:
+               case K_AUX12:
+               case K_AUX13:
+               case K_AUX14:
+               case K_AUX15:
+               case K_AUX16:
+               case K_AUX17:
+               case K_AUX18:
+               case K_AUX19:
+               case K_AUX20:
+               case K_AUX21:
+               case K_AUX22:
+               case K_AUX23:
+               case K_AUX24:
+               case K_AUX25:
+               case K_AUX26:
+               case K_AUX27:
+               case K_AUX28:
+               case K_AUX29:
+               case K_AUX30:
+               case K_AUX31:
+               case K_AUX32:
+
+               case K_KP_ENTER:
+               case K_ENTER:
+                       return K_ENTER;
+
+               case K_ESCAPE:
+               case K_JOY_BACK:
+                       return K_ESCAPE;
+       }
+
+       return key;
+}
 const char *
 Default_MenuKey(menuframework_s *m, int key)
 {
     const char *sound = NULL;
     menucommon_s *item;
+    int menu_key = Key_GetMenuKey(key);
 
     if (m)
     {
@@ -235,110 +346,51 @@ Default_MenuKey(menuframework_s *m, int key)
         }
     }
 
-    switch (key)
+    switch (menu_key)
     {
     case K_ESCAPE:
         M_PopMenu();
         return menu_out_sound;
-    case K_KP_UPARROW:
-    case K_UPARROW:
 
+    case K_UPARROW:
         if (m)
         {
             m->cursor--;
             Menu_AdjustCursor(m, -1);
             sound = menu_move_sound;
         }
-
         break;
-    case K_TAB:
 
-        if (m)
-        {
-            m->cursor++;
-            Menu_AdjustCursor(m, 1);
-            sound = menu_move_sound;
-        }
-
-        break;
-    case K_KP_DOWNARROW:
     case K_DOWNARROW:
-
         if (m)
         {
             m->cursor++;
             Menu_AdjustCursor(m, 1);
             sound = menu_move_sound;
         }
-
         break;
-    case K_KP_LEFTARROW:
-    case K_LEFTARROW:
 
+    case K_LEFTARROW:
         if (m)
         {
             Menu_SlideItem(m, -1);
             sound = menu_move_sound;
         }
-
         break;
-    case K_KP_RIGHTARROW:
-    case K_RIGHTARROW:
 
+    case K_RIGHTARROW:
         if (m)
         {
             Menu_SlideItem(m, 1);
             sound = menu_move_sound;
         }
-
         break;
 
-    case K_MOUSE1:
-    case K_MOUSE2:
-    case K_MOUSE3:
-    case K_MOUSE4:
-    case K_MOUSE5:
-    case K_AUX1:
-    case K_AUX2:
-    case K_AUX3:
-    case K_AUX4:
-    case K_AUX5:
-    case K_AUX6:
-    case K_AUX7:
-    case K_AUX8:
-    case K_AUX9:
-    case K_AUX10:
-    case K_AUX11:
-    case K_AUX12:
-    case K_AUX13:
-    case K_AUX14:
-    case K_AUX15:
-    case K_AUX16:
-    case K_AUX17:
-    case K_AUX18:
-    case K_AUX19:
-    case K_AUX20:
-    case K_AUX21:
-    case K_AUX22:
-    case K_AUX23:
-    case K_AUX24:
-    case K_AUX25:
-    case K_AUX26:
-    case K_AUX27:
-    case K_AUX28:
-    case K_AUX29:
-    case K_AUX30:
-    case K_AUX31:
-    case K_AUX32:
-
-    case K_KP_ENTER:
     case K_ENTER:
-
         if (m)
         {
             Menu_SelectItem(m);
         }
-
         sound = menu_move_sound;
         break;
     }
@@ -593,35 +645,29 @@ M_Main_Draw(void)
 const char *
 M_Main_Key(int key)
 {
-    const char *sound = menu_move_sound;
+       const char *sound = menu_move_sound;
+       int menu_key = Key_GetMenuKey(key);
 
-    switch (key)
+    switch (menu_key)
     {
     case K_ESCAPE:
         M_PopMenu();
         break;
 
-    case K_KP_DOWNARROW:
     case K_DOWNARROW:
-
         if (++m_main_cursor >= MAIN_ITEMS)
         {
             m_main_cursor = 0;
         }
-
         return sound;
 
-    case K_KP_UPARROW:
     case K_UPARROW:
-
         if (--m_main_cursor < 0)
         {
             m_main_cursor = MAIN_ITEMS - 1;
         }
-
         return sound;
 
-    case K_KP_ENTER:
     case K_ENTER:
         m_entersound = true;
 
@@ -1003,10 +1049,12 @@ static menuslider_s s_options_sensitivity_slider;
 static menulist_s s_options_freelook_box;
 static menulist_s s_options_alwaysrun_box;
 static menulist_s s_options_invertmouse_box;
-static menulist_s s_options_lookspring_box;
 static menulist_s s_options_lookstrafe_box;
 static menulist_s s_options_crosshair_box;
 static menuslider_s s_options_sfxvolume_slider;
+#ifdef SDL2
+static menuslider_s s_options_haptic_slider;
+#endif
 #if defined(OGG) || defined(CDA)
 static menulist_s s_options_cdshuffle_box;
 #endif
@@ -1023,6 +1071,14 @@ CrosshairFunc(void *unused)
     Cvar_SetValue("crosshair", (float)s_options_crosshair_box.curvalue);
 }
 
+#ifdef SDL2
+static void
+HapticMagnitudeFunc(void *unused)
+{
+    Cvar_SetValue("joy_haptic_magnitude", s_options_haptic_slider.curvalue / 
10.0F);
+}
+#endif
+
 static void
 CustomizeControlsFunc(void *unused)
 {
@@ -1097,13 +1153,15 @@ ControlsSetMenuItemValues(void)
 
     s_options_invertmouse_box.curvalue = (m_pitch->value < 0);
 
-    s_options_lookspring_box.curvalue = (lookspring->value != 0);
-
     s_options_lookstrafe_box.curvalue = (lookstrafe->value != 0);
 
     s_options_freelook_box.curvalue = (freelook->value != 0);
 
     s_options_crosshair_box.curvalue = ClampCvar(0, 3, crosshair->value);
+
+#ifdef SDL2
+    s_options_haptic_slider.curvalue = 
Cvar_VariableValue("joy_haptic_magnitude") * 10.0F;
+#endif
 }
 
 static void
@@ -1123,12 +1181,6 @@ InvertMouseFunc(void *unused)
 }
 
 static void
-LookspringFunc(void *unused)
-{
-    Cvar_SetValue("lookspring", (float)!lookspring->value);
-}
-
-static void
 LookstrafeFunc(void *unused)
 {
     Cvar_SetValue("lookstrafe", (float)!lookstrafe->value);
@@ -1319,7 +1371,11 @@ Options_MenuInit(void)
         0
     };
 
-       float scale = SCR_GetMenuScale();
+    float scale = SCR_GetMenuScale();
+
+#ifdef SDL2
+    extern qboolean show_haptic;
+#endif
 
     /* configure controls menu and menu items */
     s_options_menu.x = viddef.width / 2;
@@ -1389,34 +1445,37 @@ Options_MenuInit(void)
     s_options_invertmouse_box.generic.callback = InvertMouseFunc;
     s_options_invertmouse_box.itemnames = yesno_names;
 
-    s_options_lookspring_box.generic.type = MTYPE_SPINCONTROL;
-    s_options_lookspring_box.generic.x = 0;
-    s_options_lookspring_box.generic.y = 90;
-    s_options_lookspring_box.generic.name = "lookspring";
-    s_options_lookspring_box.generic.callback = LookspringFunc;
-    s_options_lookspring_box.itemnames = yesno_names;
-
     s_options_lookstrafe_box.generic.type = MTYPE_SPINCONTROL;
     s_options_lookstrafe_box.generic.x = 0;
-    s_options_lookstrafe_box.generic.y = 100;
+    s_options_lookstrafe_box.generic.y = 90;
     s_options_lookstrafe_box.generic.name = "lookstrafe";
     s_options_lookstrafe_box.generic.callback = LookstrafeFunc;
     s_options_lookstrafe_box.itemnames = yesno_names;
 
     s_options_freelook_box.generic.type = MTYPE_SPINCONTROL;
     s_options_freelook_box.generic.x = 0;
-    s_options_freelook_box.generic.y = 110;
+    s_options_freelook_box.generic.y = 100;
     s_options_freelook_box.generic.name = "free look";
     s_options_freelook_box.generic.callback = FreeLookFunc;
     s_options_freelook_box.itemnames = yesno_names;
 
     s_options_crosshair_box.generic.type = MTYPE_SPINCONTROL;
     s_options_crosshair_box.generic.x = 0;
-    s_options_crosshair_box.generic.y = 120;
+    s_options_crosshair_box.generic.y = 110;
     s_options_crosshair_box.generic.name = "crosshair";
     s_options_crosshair_box.generic.callback = CrosshairFunc;
     s_options_crosshair_box.itemnames = crosshair_names;
 
+#ifdef SDL2
+    s_options_haptic_slider.generic.type = MTYPE_SLIDER;
+    s_options_haptic_slider.generic.x = 0;
+    s_options_haptic_slider.generic.y = 120;
+    s_options_haptic_slider.generic.name = "haptic magnitude";
+    s_options_haptic_slider.generic.callback = HapticMagnitudeFunc;
+    s_options_haptic_slider.minvalue = 0;
+    s_options_haptic_slider.maxvalue = 22;
+#endif
+
     s_options_customize_options_action.generic.type = MTYPE_ACTION;
     s_options_customize_options_action.generic.x = 0;
     s_options_customize_options_action.generic.y = 140;
@@ -1450,10 +1509,15 @@ Options_MenuInit(void)
     Menu_AddItem(&s_options_menu, (void *)&s_options_sensitivity_slider);
     Menu_AddItem(&s_options_menu, (void *)&s_options_alwaysrun_box);
     Menu_AddItem(&s_options_menu, (void *)&s_options_invertmouse_box);
-    Menu_AddItem(&s_options_menu, (void *)&s_options_lookspring_box);
     Menu_AddItem(&s_options_menu, (void *)&s_options_lookstrafe_box);
     Menu_AddItem(&s_options_menu, (void *)&s_options_freelook_box);
     Menu_AddItem(&s_options_menu, (void *)&s_options_crosshair_box);
+
+#ifdef SDL2
+    if (show_haptic)
+        Menu_AddItem(&s_options_menu, (void *)&s_options_haptic_slider);
+#endif
+
     Menu_AddItem(&s_options_menu, (void *)&s_options_customize_options_action);
     Menu_AddItem(&s_options_menu, (void *)&s_options_defaults_action);
     Menu_AddItem(&s_options_menu, (void *)&s_options_console_action);
@@ -2305,10 +2369,10 @@ static const char *
 LoadGame_MenuKey(int key)
 {
     static menuframework_s *m = &s_loadgame_menu;
+    int menu_key = Key_GetMenuKey(key);
 
-    switch (key)
+    switch (menu_key)
     {
-    case K_KP_UPARROW:
     case K_UPARROW:
         if (m->cursor == 0)
         {
@@ -2316,8 +2380,7 @@ LoadGame_MenuKey(int key)
             LoadGame_MenuInit();
         }
         break;
-    case K_TAB:
-    case K_KP_DOWNARROW:
+
     case K_DOWNARROW:
         if (m->cursor == m->nitems - 1)
         {
@@ -2325,16 +2388,17 @@ LoadGame_MenuKey(int key)
             LoadGame_MenuInit();
         }
         break;
-    case K_KP_LEFTARROW:
+
     case K_LEFTARROW:
         LoadSave_AdjustPage(-1);
         LoadGame_MenuInit();
         return menu_move_sound;
-    case K_KP_RIGHTARROW:
+
     case K_RIGHTARROW:
         LoadSave_AdjustPage(1);
         LoadGame_MenuInit();
         return menu_move_sound;
+
     default:
         s_savegame_menu.cursor = s_loadgame_menu.cursor;
         break;
@@ -2416,6 +2480,7 @@ static const char *
 SaveGame_MenuKey(int key)
 {
     static menuframework_s *m = &s_savegame_menu;
+    int menu_key = Key_GetMenuKey(key);
 
     if (m_popup_string)
     {
@@ -2423,9 +2488,8 @@ SaveGame_MenuKey(int key)
         return NULL;
     }
 
-    switch (key)
+    switch (menu_key)
     {
-    case K_KP_UPARROW:
     case K_UPARROW:
         if (m->cursor == 0)
         {
@@ -2433,8 +2497,7 @@ SaveGame_MenuKey(int key)
             SaveGame_MenuInit();
         }
         break;
-    case K_TAB:
-    case K_KP_DOWNARROW:
+
     case K_DOWNARROW:
         if (m->cursor == m->nitems - 1)
         {
@@ -2442,16 +2505,17 @@ SaveGame_MenuKey(int key)
             SaveGame_MenuInit();
         }
         break;
-    case K_KP_LEFTARROW:
+
     case K_LEFTARROW:
         LoadSave_AdjustPage(-1);
         SaveGame_MenuInit();
         return menu_move_sound;
-    case K_KP_RIGHTARROW:
+
     case K_RIGHTARROW:
         LoadSave_AdjustPage(1);
         SaveGame_MenuInit();
         return menu_move_sound;
+
     default:
         s_loadgame_menu.cursor = s_savegame_menu.cursor;
         break;
@@ -4257,7 +4321,8 @@ M_Menu_PlayerConfig_f(void)
 static const char *
 M_Quit_Key(int key)
 {
-    switch (key)
+    int menu_key = Key_GetMenuKey(key);
+    switch (menu_key)
     {
     case K_ESCAPE:
     case 'n':
@@ -4265,6 +4330,7 @@ M_Quit_Key(int key)
         M_PopMenu();
         break;
 
+    case K_ENTER:
     case 'Y':
     case 'y':
         cls.key_dest = key_console;
diff --git a/src/client/menu/videomenu.c b/src/client/menu/videomenu.c
index 331dbf7..8511907 100644
--- a/src/client/menu/videomenu.c
+++ b/src/client/menu/videomenu.c
@@ -261,6 +261,14 @@ VID_MenuInit(void)
                "[1920 1080 ]",
                "[1920 1200 ]",
                "[2048 1536 ]",
+               "[2560x1080 ]",
+               "[2560x1440 ]",
+               "[2560x1600 ]",
+               "[3440x1440 ]",
+               "[3840x1600 ]",
+               "[3840x2160 ]",
+               "[4096x2160 ]",
+               "[5120x2880 ]",
                "[custom    ]",
                0
        };
@@ -522,8 +530,9 @@ VID_MenuKey(int key)
 
        menuframework_s *m = &s_opengl_menu;
        static const char *sound = "misc/menu1.wav";
+       int menu_key = Key_GetMenuKey(key);
 
-       switch (key)
+       switch (menu_key)
        {
                case K_ESCAPE:
                        M_PopMenu();
diff --git a/src/client/refresh/files/pcx.c b/src/client/refresh/files/pcx.c
index 010a224..926e0ea 100644
--- a/src/client/refresh/files/pcx.c
+++ b/src/client/refresh/files/pcx.c
@@ -122,7 +122,9 @@ LoadPCX(char *origname, byte **pic, byte **palette, int 
*width, int *height)
        byte *raw;
        pcx_t *pcx;
        int x, y;
-       int len;
+       int len, full_size;
+       int pcx_width, pcx_height;
+       qboolean image_issues = false;
        int dataByte, runLength;
        byte *out, *pix;
        char filename[256];
@@ -145,7 +147,7 @@ LoadPCX(char *origname, byte **pic, byte **palette, int 
*width, int *height)
        /* load the file */
        len = ri.FS_LoadFile(filename, (void **)&raw);
 
-       if (!raw)
+       if (!raw || len < sizeof(pcx_t))
        {
                R_Printf(PRINT_DEVELOPER, "Bad pcx file %s\n", filename);
                return;
@@ -165,15 +167,19 @@ LoadPCX(char *origname, byte **pic, byte **palette, int 
*width, int *height)
 
        raw = &pcx->data;
 
+       pcx_width = pcx->xmax - pcx->xmin;
+       pcx_height = pcx->ymax - pcx->ymin;
+
        if ((pcx->manufacturer != 0x0a) || (pcx->version != 5) ||
                (pcx->encoding != 1) || (pcx->bits_per_pixel != 8) ||
-               (pcx->xmax >= 640) || (pcx->ymax >= 480))
+               (pcx_width >= 4096) || (pcx_height >= 4096))
        {
                R_Printf(PRINT_ALL, "Bad pcx file %s\n", filename);
                return;
        }
 
-       out = malloc((pcx->ymax + 1) * (pcx->xmax + 1));
+       full_size = (pcx_height + 1) * (pcx_width + 1);
+       out = malloc(full_size);
 
        *pic = out;
 
@@ -182,28 +188,49 @@ LoadPCX(char *origname, byte **pic, byte **palette, int 
*width, int *height)
        if (palette)
        {
                *palette = malloc(768);
-               memcpy(*palette, (byte *)pcx + len - 768, 768);
+               if (len > 768)
+               {
+                       memcpy(*palette, (byte *)pcx + len - 768, 768);
+               }
+               else
+               {
+                       image_issues = true;
+               }
        }
 
        if (width)
        {
-               *width = pcx->xmax + 1;
+               *width = pcx_width + 1;
        }
 
        if (height)
        {
-               *height = pcx->ymax + 1;
+               *height = pcx_height + 1;
        }
 
-       for (y = 0; y <= pcx->ymax; y++, pix += pcx->xmax + 1)
+       for (y = 0; y <= pcx_height; y++, pix += pcx_width + 1)
        {
-               for (x = 0; x <= pcx->xmax; )
+               for (x = 0; x <= pcx_width; )
                {
+                       if (raw - (byte *)pcx > len)
+                       {
+                               // no place for read
+                               image_issues = true;
+                               x = pcx_width;
+                               break;
+                       }
                        dataByte = *raw++;
 
                        if ((dataByte & 0xC0) == 0xC0)
                        {
                                runLength = dataByte & 0x3F;
+                               if (raw - (byte *)pcx > len)
+                               {
+                                       // no place for read
+                                       image_issues = true;
+                                       x = pcx_width;
+                                       break;
+                               }
                                dataByte = *raw++;
                        }
                        else
@@ -213,7 +240,17 @@ LoadPCX(char *origname, byte **pic, byte **palette, int 
*width, int *height)
 
                        while (runLength-- > 0)
                        {
-                               pix[x++] = dataByte;
+                               if ((*pic + full_size) <= (pix + x))
+                               {
+                                       // no place for write
+                                       image_issues = true;
+                                       x += runLength;
+                                       runLength = 0;
+                               }
+                               else
+                               {
+                                       pix[x++] = dataByte;
+                               }
                        }
                }
        }
@@ -224,7 +261,7 @@ LoadPCX(char *origname, byte **pic, byte **palette, int 
*width, int *height)
                free(*pic);
                *pic = NULL;
        }
-       else if(pcx->xmax == 319 && pcx->ymax == 239
+       else if(pcx_width == 319 && pcx_height == 239
                        && Q_strcasecmp(origname, "pics/quit.pcx") == 0
                        && Com_BlockChecksum(pcx, len) == 3329419434u)
        {
@@ -233,6 +270,11 @@ LoadPCX(char *origname, byte **pic, byte **palette, int 
*width, int *height)
                fixQuitScreen(*pic);
        }
 
+       if (image_issues)
+       {
+               R_Printf(PRINT_ALL, "PCX file %s has possible size issues.\n", 
filename);
+       }
+
        ri.FS_FreeFile(pcx);
 }
 
diff --git a/src/client/refresh/gl/r_main.c b/src/client/refresh/gl/r_main.c
index 003465b..bc0e213 100644
--- a/src/client/refresh/gl/r_main.c
+++ b/src/client/refresh/gl/r_main.c
@@ -1344,6 +1344,17 @@ R_SetMode(void)
                else if (err == rserr_invalid_mode)
                {
                        R_Printf(PRINT_ALL, "ref_gl::R_SetMode() - invalid 
mode\n");
+                       if (gl_msaa_samples->value != 0.0f)
+                       {
+                               R_Printf(PRINT_ALL, "gl_msaa_samples was %d - 
will try again with gl_msaa_samples = 0\n", (int)gl_msaa_samples->value);
+                               ri.Cvar_SetValue("gl_msaa_samples", 0.0f);
+                               gl_msaa_samples->modified = false;
+
+                               if ((err = SetMode_impl(&vid.width, 
&vid.height, gl_mode->value, 0)) == rserr_ok)
+                               {
+                                       return true;
+                               }
+                       }
                        if(gl_mode->value == gl_state.prev_mode)
                        {
                                // trying again would result in a crash anyway, 
give up already
diff --git a/src/client/refresh/gl/r_sdl.c b/src/client/refresh/gl/r_sdl.c
index dd64c3c..7a6f261 100644
--- a/src/client/refresh/gl/r_sdl.c
+++ b/src/client/refresh/gl/r_sdl.c
@@ -360,6 +360,11 @@ int RI_PrepareForWindow(void)
                        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
                }
        }
+       else
+       {
+               SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
+               SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
+       }
 
        /* Initiate the flags */
 #if SDL_VERSION_ATLEAST(2, 0, 0)
@@ -410,6 +415,14 @@ int RI_InitContext(void* win)
 
 #endif
 
+       const char* glver = (char *)glGetString(GL_VERSION);
+       sscanf(glver, "%d.%d", &gl_config.major_version, 
&gl_config.minor_version);
+       if (gl_config.major_version < 1 || (gl_config.major_version == 1 && 
gl_config.minor_version < 4))
+       {
+               R_Printf(PRINT_ALL, "R_InitContext(): Got an OpenGL version 
%d.%d context - need (at least) 1.4!\n", gl_config.major_version, 
gl_config.minor_version);
+               return false;
+       }
+
        if (gl_msaa_samples->value)
        {
                if (SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, 
&msaa_samples) == 0)
diff --git a/src/client/refresh/gl3/gl3_main.c 
b/src/client/refresh/gl3/gl3_main.c
index b40cff0..ea517d3 100644
--- a/src/client/refresh/gl3/gl3_main.c
+++ b/src/client/refresh/gl3/gl3_main.c
@@ -406,6 +406,17 @@ GL3_SetMode(void)
                {
                        R_Printf(PRINT_ALL, "ref_gl3::GL3_SetMode() - invalid 
mode\n");
 
+                       if (gl_msaa_samples->value != 0.0f)
+                       {
+                               R_Printf(PRINT_ALL, "gl_msaa_samples was %d - 
will try again with gl_msaa_samples = 0\n", (int)gl_msaa_samples->value);
+                               ri.Cvar_SetValue("gl_msaa_samples", 0.0f);
+                               gl_msaa_samples->modified = false;
+
+                               if ((err = SetMode_impl(&vid.width, 
&vid.height, gl_mode->value, 0)) == rserr_ok)
+                               {
+                                       return true;
+                               }
+                       }
                        if(gl_mode->value == gl3state.prev_mode)
                        {
                                // trying again would result in a crash anyway, 
give up already
diff --git a/src/client/refresh/gl3/gl3_sdl.c b/src/client/refresh/gl3/gl3_sdl.c
index 45519c6..c932364 100644
--- a/src/client/refresh/gl3/gl3_sdl.c
+++ b/src/client/refresh/gl3/gl3_sdl.c
@@ -113,6 +113,11 @@ int GL3_PrepareForWindow(void)
                        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
                }
        }
+       else
+       {
+               SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
+               SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
+       }
 
        /* Initiate the flags */
 #if SDL_VERSION_ATLEAST(2, 0, 0)
@@ -131,7 +136,7 @@ enum {
        QGL_DEBUG_SEVERITY_NOTIFICATION = 0x826B
 };
 
-static void
+static void APIENTRY
 DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei 
length,
               const GLchar *message, const void *userParam)
 {
@@ -230,12 +235,17 @@ int GL3_InitContext(void* win)
 
        if(!gladLoadGLLoader(SDL_GL_GetProcAddress))
        {
-               R_Printf(PRINT_ALL, "R_InitContext(): loading OpenGL function 
pointers failed!\n");
+               R_Printf(PRINT_ALL, "GL3_InitContext(): ERROR: loading OpenGL 
function pointers failed!\n");
+               return false;
+       }
+       else if (GLVersion.major < 3 || (GLVersion.major == 3 && 
GLVersion.minor < 2))
+       {
+               R_Printf(PRINT_ALL, "GL3_InitContext(): ERROR: glad only got GL 
version %d.%d!\n", GLVersion.major, GLVersion.minor);
                return false;
        }
        else
        {
-               R_Printf(PRINT_ALL, "Successfully loaded OpenGL function 
pointers using glad!\n");
+               R_Printf(PRINT_ALL, "Successfully loaded OpenGL function 
pointers using glad, got version %d.%d!\n", GLVersion.major, GLVersion.minor);
        }
 
 #if SDL_VERSION_ATLEAST(2, 0, 0)
diff --git a/src/client/sound/ogg.c b/src/client/sound/ogg.c
index 4f521f7..35fab85 100644
--- a/src/client/sound/ogg.c
+++ b/src/client/sound/ogg.c
@@ -636,18 +636,12 @@ OGG_Stream(void)
                        /* Calculate the number of buffers used
                           for storing decoded OGG/Vorbis data.
                           We take the number of active buffers
-                          at startup (at this point most of the
-                          samples should be precached and loaded
-                          into buffers) and add 64. Empircal
-                          testing showed, that at most times
-                          at least 52 buffers remain available
-                          for OGG/Vorbis, enough for about 3
-                          seconds playback. The music won't
-                          stutter as long as the framerate
-                          stayes over 1 FPS. */
-                       if (ogg_numbufs == 0)
+                          and add 256. 256 are about 12 seconds
+                          worth of sound, more than enough to
+                          be resilent against underruns. */
+                       if (ogg_numbufs == 0 || active_buffers < ogg_numbufs - 
256)
                        {
-                               ogg_numbufs = active_buffers + 64;
+                               ogg_numbufs = active_buffers + 256;
                        }
 
                        /* active_buffers are all active OpenAL buffers,
@@ -761,7 +755,10 @@ OGG_PauseCmd(void)
        }
 
 #ifdef USE_OPENAL
-       AL_UnqueueRawSamples();
+       if (sound_started == SS_OAL)
+       {
+               AL_UnqueueRawSamples();
+       }
 #endif
 }
 
diff --git a/src/client/sound/sound.c b/src/client/sound/sound.c
index 6f98c39..36d384d 100644
--- a/src/client/sound/sound.c
+++ b/src/client/sound/sound.c
@@ -669,6 +669,16 @@ S_StartSound(vec3_t origin, int entnum, int entchannel, 
sfx_t *sfx,
                ps->fixed_origin = false;
        }
 
+       if (sfx->name[0])
+       {
+               // with !fixed we have all sounds related directly to player,
+               // e.g. players fire, pain, menu
+               if (!ps->fixed_origin)
+               {
+                       Haptic_Feedback(sfx->name);
+               }
+       }
+
        ps->entnum = entnum;
        ps->entchannel = entchannel;
        ps->attenuation = attenuation;
diff --git a/src/common/collision.c b/src/common/collision.c
index f494013..bdaa5e4 100644
--- a/src/common/collision.c
+++ b/src/common/collision.c
@@ -119,7 +119,7 @@ vec3_t trace_extents;
 int            c_pointcontents;
 int            c_traces, c_brush_traces;
 #endif
- 
+
 /* 1/32 epsilon to keep floating point happy */
 #define DIST_EPSILON (0.03125f)
 
@@ -738,7 +738,7 @@ CM_ClipBoxToBrush(vec3_t mins, vec3_t maxs, vec3_t p1,
 }
 
 void
-CM_TestBoxInBrush(vec3_t mins, vec3_t maxs, vec3_t p1, 
+CM_TestBoxInBrush(vec3_t mins, vec3_t maxs, vec3_t p1,
                trace_t *trace, cbrush_t *brush)
 {
        int i, j;
@@ -1186,7 +1186,7 @@ CMod_LoadSubmodels(lump_t *l)
 
        if (l->filelen % sizeof(*in))
        {
-               Com_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size");
+               Com_Error(ERR_DROP, "Mod_LoadSubmodels: funny lump size");
        }
 
        count = l->filelen / sizeof(*in);
@@ -1230,7 +1230,7 @@ CMod_LoadSurfaces(lump_t *l)
 
        if (l->filelen % sizeof(*in))
        {
-               Com_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size");
+               Com_Error(ERR_DROP, "Mod_LoadSurfaces: funny lump size");
        }
 
        count = l->filelen / sizeof(*in);
@@ -1269,7 +1269,7 @@ CMod_LoadNodes(lump_t *l)
 
        if (l->filelen % sizeof(*in))
        {
-               Com_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size");
+               Com_Error(ERR_DROP, "Mod_LoadNodes: funny lump size");
        }
 
        count = l->filelen / sizeof(*in);
@@ -1311,7 +1311,7 @@ CMod_LoadBrushes(lump_t *l)
 
        if (l->filelen % sizeof(*in))
        {
-               Com_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size");
+               Com_Error(ERR_DROP, "Mod_LoadBrushes: funny lump size");
        }
 
        count = l->filelen / sizeof(*in);
@@ -1345,7 +1345,7 @@ CMod_LoadLeafs(lump_t *l)
 
        if (l->filelen % sizeof(*in))
        {
-               Com_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size");
+               Com_Error(ERR_DROP, "Mod_LoadLeafs: funny lump size");
        }
 
        count = l->filelen / sizeof(*in);
@@ -1415,7 +1415,7 @@ CMod_LoadPlanes(lump_t *l)
 
        if (l->filelen % sizeof(*in))
        {
-               Com_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size");
+               Com_Error(ERR_DROP, "Mod_LoadPlanes: funny lump size");
        }
 
        count = l->filelen / sizeof(*in);
@@ -1466,7 +1466,7 @@ CMod_LoadLeafBrushes(lump_t *l)
 
        if (l->filelen % sizeof(*in))
        {
-               Com_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size");
+               Com_Error(ERR_DROP, "Mod_LoadLeafBrushes: funny lump size");
        }
 
        count = l->filelen / sizeof(*in);
@@ -1504,7 +1504,7 @@ CMod_LoadBrushSides(lump_t *l)
 
        if (l->filelen % sizeof(*in))
        {
-               Com_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size");
+               Com_Error(ERR_DROP, "Mod_LoadBrushSides: funny lump size");
        }
 
        count = l->filelen / sizeof(*in);
@@ -1545,7 +1545,7 @@ CMod_LoadAreas(lump_t *l)
 
        if (l->filelen % sizeof(*in))
        {
-               Com_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size");
+               Com_Error(ERR_DROP, "Mod_LoadAreas: funny lump size");
        }
 
        count = l->filelen / sizeof(*in);
@@ -1578,7 +1578,7 @@ CMod_LoadAreaPortals(lump_t *l)
 
        if (l->filelen % sizeof(*in))
        {
-               Com_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size");
+               Com_Error(ERR_DROP, "Mod_LoadAreaPortals: funny lump size");
        }
 
        count = l->filelen / sizeof(*in);
diff --git a/src/common/frame.c b/src/common/frame.c
index 1fcc90f..606ac14 100644
--- a/src/common/frame.c
+++ b/src/common/frame.c
@@ -293,6 +293,22 @@ Qcommon_Frame(int msec)
                c_pointcontents = 0;
        }
 
+       // gl_maxfps > 1000 breaks things, and so does <= 0
+       // so cap to 1000 and treat <= 0 as "as fast as possible", which is 1000
+       if (gl_maxfps->value > 1000 || gl_maxfps->value < 1)
+       {
+               Cvar_SetValue("gl_maxfps", 1000);
+       }
+
+       if(cl_maxfps->value > 250)
+       {
+               Cvar_SetValue("cl_maxfps", 130);
+       }
+       else if(cl_maxfps->value < 1)
+       {
+               Cvar_SetValue("cl_maxfps", 60);
+       }
+
 
        // Save global time for network- und input code.
        curtime = Sys_Milliseconds();
diff --git a/src/common/header/common.h b/src/common/header/common.h
index 2f8afe2..2d6c598 100644
--- a/src/common/header/common.h
+++ b/src/common/header/common.h
@@ -32,7 +32,7 @@
 #include "shared.h"
 #include "crc.h"
 
-#define YQ2VERSION "7.02"
+#define YQ2VERSION "7.10"
 #define BASEDIRNAME "baseq2"
 
 #ifndef YQ2OSTYPE
diff --git a/src/common/header/shared.h b/src/common/header/shared.h
index b6813c0..d12ab68 100644
--- a/src/common/header/shared.h
+++ b/src/common/header/shared.h
@@ -457,8 +457,8 @@ typedef struct cmodel_s
 typedef struct csurface_s
 {
        char name[16];
-       int flags;
-       int value;
+       int flags; /* SURF_* */
+       int value; /* unused */
 } csurface_t;
 
 typedef struct mapsurface_s  /* used internally due to name len probs */

-- 
Alioth's /usr/local/bin/git-commit-notice on 
/srv/git.debian.org/git/pkg-games/yquake2.git

_______________________________________________
Pkg-games-commits mailing list
[email protected]
http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-games-commits

Reply via email to