/*
Copyright (C) 1996-1997 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 the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/vt.h>
#include <stdarg.h>
#include <stdio.h>
#include <signal.h>
#include <asm/io.h>

//#include <X11/cursorfont.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>

#include "GL/gl.h"
#include "GL/glx.h"

#include "quakedef.h"

#define WARP_WIDTH              320
#define WARP_HEIGHT             200

static Display *dpy = NULL;
static Window win;
static GLXContext ctx = NULL;

unsigned short d_8to16table[256];
unsigned int d_8to24table[256];
unsigned char d_15to8table[65536];

static qboolean usedga = false;

#define stringify(m) { #m, m }

cvar_t vid_mode = {"vid_mode","0",false};
 
cvar_t  mouse_button_commands[3] =
{
    {"mouse1","+attack"},
    {"mouse2","+strafe"},
    {"mouse3","+forward"},
};

static int mouse_buttons=3;
static int mouse_buttonstate;
static int mouse_oldbuttonstate;
static float mouse_x, mouse_y;
static float p_mouse_x, p_mouse_y;
static float old_mouse_x, old_mouse_y;

cvar_t _windowed_mouse = {"_windowed_mouse", "1", true};
cvar_t m_filter = {"m_filter","0"};
static float old_windowed_mouse;

static int scr_width, scr_height;

#define KEY_MASK (KeyPressMask | KeyReleaseMask)
#define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | \
        PointerMotionMask | ButtonMotionMask)

/*-----------------------------------------------------------------------*/

//int   texture_mode = GL_NEAREST;
//int   texture_mode = GL_NEAREST_MIPMAP_NEAREST;
//int   texture_mode = GL_NEAREST_MIPMAP_LINEAR;
int   texture_mode = GL_LINEAR;
//int   texture_mode = GL_LINEAR_MIPMAP_NEAREST;
//int   texture_mode = GL_LINEAR_MIPMAP_LINEAR;

int   texture_extension_number = 1;

float   gldepthmin, gldepthmax;

cvar_t  gl_ztrick = {"gl_ztrick","1"};

const char *gl_vendor;
const char *gl_renderer;
const char *gl_version;
const char *gl_extensions;

qboolean is8bit = false;
qboolean isPermedia = false;
qboolean gl_mtexable = false;

/*-----------------------------------------------------------------------*/
void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height)
{
}

void D_EndDirectRect (int x, int y, int width, int height)
{
}

/*
=================
VID_Gamma_f

Keybinding command
=================
*/
void VID_Gamma_f (void)
{
  float gamma, f, inf;
  unsigned char palette[768];
  int   i;

  if (Cmd_Argc () == 2)
  {
    gamma = Q_atof (Cmd_Argv(1));

    for (i=0 ; i<768 ; i++)
    {
      f = pow ( (host_basepal[i]+1)/256.0 , gamma );
      inf = f*255 + 0.5;
      if (inf < 0)
        inf = 0;
      if (inf > 255)
        inf = 255;
      palette[i] = inf;
    }

    VID_SetPalette (palette);

    vid.recalc_refdef = 1;        // force a surface cache flush
  }
}

void VID_Shutdown(void)
{
  if (!ctx)
    return;

  XUngrabPointer(dpy,CurrentTime);
  XUngrabKeyboard(dpy,CurrentTime);

  glXDestroyContext(dpy,ctx);

#ifdef USE_DGA
  if (usedga)
    XF86DGADirectVideo(dpy,DefaultScreen(dpy),0);
#endif
}

int XLateKey(XKeyEvent *ev)
{

  int key;
  char buf[64];
  KeySym keysym;

  key = 0;

  XLookupString(ev, buf, sizeof buf, &keysym, 0);

  switch(keysym)
  {
    case XK_KP_Page_Up:
    case XK_Page_Up:   key = K_PGUP; break;

    case XK_KP_Page_Down:
    case XK_Page_Down:   key = K_PGDN; break;

    case XK_KP_Home:
    case XK_Home:  key = K_HOME; break;

    case XK_KP_End:
    case XK_End:   key = K_END; break;

    case XK_KP_Left:
    case XK_Left:  key = K_LEFTARROW; break;

    case XK_KP_Right:
    case XK_Right:  key = K_RIGHTARROW;   break;

    case XK_KP_Down:
    case XK_Down:  key = K_DOWNARROW; break;

    case XK_KP_Up:
    case XK_Up:    key = K_UPARROW;  break;

    case XK_Escape: key = K_ESCAPE;   break;

    case XK_KP_Enter:
    case XK_Return: key = K_ENTER;     break;

    case XK_Tab:    key = K_TAB;       break;

    case XK_F1:    key = K_F1;        break;

    case XK_F2:    key = K_F2;        break;

    case XK_F3:    key = K_F3;        break;

    case XK_F4:    key = K_F4;        break;

    case XK_F5:    key = K_F5;        break;

    case XK_F6:    key = K_F6;        break;

    case XK_F7:    key = K_F7;        break;

    case XK_F8:    key = K_F8;        break;

    case XK_F9:    key = K_F9;        break;

    case XK_F10:    key = K_F10;       break;

    case XK_F11:    key = K_F11;       break;

    case XK_F12:    key = K_F12;       break;

    case XK_BackSpace: key = K_BACKSPACE; break;

    case XK_KP_Delete:
    case XK_Delete: key = K_DEL; break;

    case XK_Pause:  key = K_PAUSE;     break;

    case XK_Shift_L:
    case XK_Shift_R:  key = K_SHIFT;    break;

    case XK_Execute: 
    case XK_Control_L: 
    case XK_Control_R:  key = K_CTRL;    break;

    case XK_Alt_L:  
    case XK_Meta_L: 
    case XK_Alt_R:  
    case XK_Meta_R: key = K_ALT;      break;

    case XK_KP_Begin: key = K_AUX30;  break;

    case XK_Insert:
    case XK_KP_Insert: key = K_INS; break;

    case XK_KP_Multiply: key = '*'; break;
    case XK_KP_Add: key = '+'; break;
    case XK_KP_Subtract: key = '-'; break;
    case XK_KP_Divide: key = '/'; break;

#if 0
    case 0x021: key = '1';break;/* [!] */
    case 0x040: key = '2';break;/* [@] */
    case 0x023: key = '3';break;/* [#] */
    case 0x024: key = '4';break;/* [$] */
    case 0x025: key = '5';break;/* [%] */
    case 0x05e: key = '6';break;/* [^] */
    case 0x026: key = '7';break;/* [&] */
    case 0x02a: key = '8';break;/* [*] */
    case 0x028: key = '9';;break;/* [(] */
    case 0x029: key = '0';break;/* [)] */
    case 0x05f: key = '-';break;/* [_] */
    case 0x02b: key = '=';break;/* [+] */
    case 0x07c: key = '\'';break;/* [|] */
    case 0x07d: key = '[';break;/* [}] */
    case 0x07b: key = ']';break;/* [{] */
    case 0x022: key = '\'';break;/* ["] */
    case 0x03a: key = ';';break;/* [:] */
    case 0x03f: key = '/';break;/* [?] */
    case 0x03e: key = '.';break;/* [>] */
    case 0x03c: key = ',';break;/* [<] */
#endif

    default:
      key = *(unsigned char*)buf;
      if (key >= 'A' && key <= 'Z')
        key = key - 'A' + 'a';
//      fprintf(stdout, "case 0x0%x: key = ___;break;/* [%c] */\n", keysym);
      break;
  } 

  return key;
}

struct
{
  int key;
  int down;
} keyq[64];
int keyq_head=0;
int keyq_tail=0;

int config_notify=0;
int config_notify_width;
int config_notify_height;
                  
qboolean Keyboard_Update(void)
{
  XEvent x_event;
   
  if(!XCheckMaskEvent(dpy,KEY_MASK,&x_event))
    return false;

  switch(x_event.type) {
  case KeyPress:
    keyq[keyq_head].key = XLateKey(&x_event.xkey);
    keyq[keyq_head].down = true;
    keyq_head = (keyq_head + 1) & 63;
    break;
  case KeyRelease:
    keyq[keyq_head].key = XLateKey(&x_event.xkey);
    keyq[keyq_head].down = false;
    keyq_head = (keyq_head + 1) & 63;
    break;
  }

  return true;
}

qboolean Mouse_Update(void)
{
  XEvent x_event;
  int b;
   
  if(!XCheckMaskEvent(dpy,MOUSE_MASK,&x_event))
    return false;

  switch(x_event.type) {
  case MotionNotify:
    if (usedga) {
      mouse_x += x_event.xmotion.x_root;
      mouse_y += x_event.xmotion.y_root;
    } else if (_windowed_mouse.value) {
      mouse_x += (float) ((int)x_event.xmotion.x - (int)(scr_width/2));
      mouse_y += (float) ((int)x_event.xmotion.y - (int)(scr_height/2));

      /* move the mouse to the window center again */
      XSelectInput(dpy,win, (KEY_MASK | MOUSE_MASK) & ~PointerMotionMask);
      XWarpPointer(dpy,None,win,0,0,0,0, (scr_width/2),(scr_height/2));
      XSelectInput(dpy,win, KEY_MASK | MOUSE_MASK);
    } else {
      mouse_x = (float) (x_event.xmotion.x-p_mouse_x);
      mouse_y = (float) (x_event.xmotion.y-p_mouse_y);
      p_mouse_x=x_event.xmotion.x;
      p_mouse_y=x_event.xmotion.y;
    }
    break;

  case ButtonPress:
    b=-1;
    if (x_event.xbutton.button == 1)
      b = 0;
    else if (x_event.xbutton.button == 2)
      b = 2;
    else if (x_event.xbutton.button == 3)
      b = 1;
    if (b>=0)
      mouse_buttonstate |= 1<<b;
    break;

  case ButtonRelease:
    b=-1;
    if (x_event.xbutton.button == 1)
      b = 0;
    else if (x_event.xbutton.button == 2)
      b = 2;
    else if (x_event.xbutton.button == 3)
      b = 1;
    if (b>=0)
      mouse_buttonstate &= ~(1<<b);
    break;
  }
   
  if (old_windowed_mouse != _windowed_mouse.value) {
    old_windowed_mouse = _windowed_mouse.value;

    if (!_windowed_mouse.value) {
      /* ungrab the pointer */
      Con_Printf("Releasing mouse.\n");

      XUngrabPointer(dpy,CurrentTime);
      XUngrabKeyboard(dpy,CurrentTime);
    } else {
      /* grab the pointer */
      Con_Printf("Grabbing mouse.\n");

      XGrabPointer(dpy,win,False,MOUSE_MASK,GrabModeAsync,
        GrabModeAsync,win,None,CurrentTime);
      XWarpPointer(dpy,None,win, 0,0,0,0, scr_width/2, scr_height/2);
      XGrabKeyboard(dpy,win,
        False,
        GrabModeAsync,GrabModeAsync,
        CurrentTime);

      //XSync(dpy,True);
    }
  }
  return true;
}

void signal_handler(int sig)
{
  printf("Received signal %d, exiting...\n", sig);
  VID_Shutdown();
  exit(0);
}

void InitSig(void)
{
  signal(SIGHUP, signal_handler);
  signal(SIGQUIT, signal_handler);
  signal(SIGILL, signal_handler);
  signal(SIGTRAP, signal_handler);
  signal(SIGIOT, signal_handler);
  signal(SIGBUS, signal_handler);
  signal(SIGFPE, signal_handler);
  signal(SIGSEGV, signal_handler);
  signal(SIGTERM, signal_handler);
}

void VID_ShiftPalette(unsigned char *p)
{
  VID_SetPalette(p);
}

void  VID_SetPalette (unsigned char *palette)
{
  byte  *pal;
  unsigned short r,g,b;
  int     v;
  int     r1,g1,b1;
  int   k;
  unsigned short i;
  unsigned  *table;
  FILE *f;
  char s[255];
  float dist, bestdist;
  static qboolean palflag = false;

//
// 8 8 8 encoding
//
  pal = palette;
  table = d_8to24table;
  for (i=0 ; i<256 ; i++)
  {
    r = pal[0];
    g = pal[1];
    b = pal[2];
    pal += 3;
    
//    v = (255<<24) + (r<<16) + (g<<8) + (b<<0);
//    v = (255<<0) + (r<<8) + (g<<16) + (b<<24);
    v = (255<<24) + (r<<0) + (g<<8) + (b<<16);
    *table++ = v;
  }
  d_8to24table[255] &= 0xffffff;  // 255 is transparent

  // JACK: 3D distance calcs - k is last closest, l is the distance.
  // FIXME: Precalculate this and cache to disk.
  if (palflag)
    return;
  palflag = true;

  COM_FOpenFile("glquake/15to8.pal", &f);
  if (f) {
    fread(d_15to8table, 1<<15, 1, f);
    fclose(f);
  } else {
    for (i=0; i < (1<<15); i++) {
      /* Maps
      000000000000000
      000000000011111 = Red  = 0x1F
      000001111100000 = Blue = 0x03E0
      111110000000000 = Grn  = 0x7C00
      */
      r = ((i & 0x1F) << 3)+4;
      g = ((i & 0x03E0) >> 2)+4;
      b = ((i & 0x7C00) >> 7)+4;
      pal = (unsigned char *)d_8to24table;
      for (v=0,k=0,bestdist=10000.0; v<256; v++,pal+=4) {
        r1 = (int)r - (int)pal[0];
        g1 = (int)g - (int)pal[1];
        b1 = (int)b - (int)pal[2];
        dist = sqrt(((r1*r1)+(g1*g1)+(b1*b1)));
        if (dist < bestdist) {
          k=v;
          bestdist = dist;
        }
      }
      d_15to8table[i]=k;
    }
    sprintf(s, "%s/glquake", com_gamedir);
    Sys_mkdir (s);
    sprintf(s, "%s/glquake/15to8.pal", com_gamedir);
    if ((f = fopen(s, "wb")) != NULL) {
      fwrite(d_15to8table, 1<<15, 1, f);
      fclose(f);
    }
  }
}

/*
===============
GL_Init
===============
*/
void GL_Init (void)
{
  gl_vendor = glGetString (GL_VENDOR);
  Con_Printf ("GL_VENDOR: %s\n", gl_vendor);
  gl_renderer = glGetString (GL_RENDERER);
  Con_Printf ("GL_RENDERER: %s\n", gl_renderer);

  gl_version = glGetString (GL_VERSION);
  Con_Printf ("GL_VERSION: %s\n", gl_version);
  gl_extensions = glGetString (GL_EXTENSIONS);
  Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions);

//  Con_Printf ("%s %s\n", gl_renderer, gl_version);

  glClearColor (1,0,0,0);
  glCullFace(GL_FRONT);
  glEnable(GL_TEXTURE_2D);

  glEnable(GL_ALPHA_TEST);
  glAlphaFunc(GL_GREATER, 0.666);

  glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
  glShadeModel (GL_FLAT);

  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

//  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}

/*
=================
GL_BeginRendering

=================
*/
void GL_BeginRendering (int *x, int *y, int *width, int *height)
{
  extern cvar_t gl_clear;

  *x = *y = 0;
  *width = scr_width;
  *height = scr_height;

//    if (!wglMakeCurrent( maindc, baseRC ))
//    Sys_Error ("wglMakeCurrent failed");

//  glViewport (*x, *y, *width, *height);
}


void GL_EndRendering (void)
{
  glFlush();
  glXSwapBuffers(dpy,win);
}

qboolean VID_Is8bit(void)
{
  return is8bit;
}

#ifdef GL_EXT_SHARED
void VID_Init8bitPalette() 
{
  // Check for 8bit Extensions and initialize them.
  int i;
  char thePalette[256*3];
  char *oldPalette, *newPalette;

  if (strstr(gl_extensions, "GL_EXT_shared_texture_palette") == NULL)
    return;

  Con_SafePrintf("8-bit GL extensions enabled.\n");
  glEnable( GL_SHARED_TEXTURE_PALETTE_EXT );
  oldPalette = (char *) d_8to24table; //d_8to24table3dfx;
  newPalette = thePalette;
  for (i=0;i<256;i++) {
    *newPalette++ = *oldPalette++;
    *newPalette++ = *oldPalette++;
    *newPalette++ = *oldPalette++;
    oldPalette++;
  }
  glColorTableEXT(GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE, (void *) thePalette);
  is8bit = true;
}

#else
extern void gl3DfxSetPaletteEXT(GLuint *pal);

void VID_Init8bitPalette(void) 
{
  // Check for 8bit Extensions and initialize them.
  int i;
  GLubyte table[256][4];
  char *oldpal;

  if (strstr(gl_extensions, "3DFX_set_global_palette") == NULL)
    return;

  Con_SafePrintf("8-bit GL extensions enabled.\n");
  glEnable( GL_SHARED_TEXTURE_PALETTE_EXT );
  oldpal = (char *) d_8to24table; //d_8to24table3dfx;
  for (i=0;i<256;i++) {
    table[i][2] = *oldpal++;
    table[i][1] = *oldpal++;
    table[i][0] = *oldpal++;
    table[i][3] = 255;
    oldpal++;
  }
  gl3DfxSetPaletteEXT((GLuint *)table);
  is8bit = true;
}
#endif

void VID_Init(unsigned char *palette)
{
  int i;
  char  gldir[MAX_OSPATH];
  int width = 640, height = 480;
  int attrib[] = { 
    GLX_RGBA,
    GLX_RED_SIZE, 1,
    GLX_GREEN_SIZE, 1,
    GLX_BLUE_SIZE, 1,
    GLX_DOUBLEBUFFER,
    GLX_DEPTH_SIZE, 1,
    None };
  int scrnum;
  XSetWindowAttributes attr;
  unsigned long mask;
  Window root;
  XVisualInfo *visinfo;

  S_Init();

  Cvar_RegisterVariable (&vid_mode);
  Cvar_RegisterVariable (&gl_ztrick);
  
  vid.maxwarpwidth = WARP_WIDTH;
  vid.maxwarpheight = WARP_HEIGHT;
  vid.colormap = host_colormap;
  vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048));

// interpret command-line params

// set vid parameters

  if ((i = COM_CheckParm("-width")) != 0)
    width = atoi(com_argv[i+1]);
  if ((i = COM_CheckParm("-height")) != 0)
    height = atoi(com_argv[i+1]);

  if ((i = COM_CheckParm("-conwidth")) != 0)
    vid.conwidth = Q_atoi(com_argv[i+1]);
  else
    vid.conwidth = 640;

  vid.conwidth &= 0xfff8; // make it a multiple of eight

  if (vid.conwidth < 320)
    vid.conwidth = 320;

  // pick a conheight that matches with correct aspect
  vid.conheight = vid.conwidth*3 / 4;

  if ((i = COM_CheckParm("-conheight")) != 0)
    vid.conheight = Q_atoi(com_argv[i+1]);
  if (vid.conheight < 200)
    vid.conheight = 200;

  if (!(dpy = XOpenDisplay(NULL))) {
    fprintf(stderr, "Error couldn't open the X display\n");
    exit(1);
  }

  scrnum = DefaultScreen(dpy);
  root = RootWindow(dpy, scrnum);

  visinfo=glXChooseVisual(dpy,scrnum,attrib);
  if (!visinfo) {
    fprintf(stderr, "Error couldn't get an RGB, Double-buffered, Depth visual\n");
    exit(1);
  }

  /* window attributes */
  attr.background_pixel=0;
  attr.border_pixel=0;
  attr.colormap=XCreateColormap(dpy,root,visinfo->visual,AllocNone);
  attr.event_mask=KEY_MASK|MOUSE_MASK|VisibilityChangeMask;
  mask=CWBackPixel|CWBorderPixel|CWColormap|CWEventMask;

  win=XCreateWindow(dpy,root,0,0,width,height,
      0,visinfo->depth,InputOutput,
      visinfo->visual,mask,&attr);
  XMapWindow(dpy,win);

  XMoveWindow(dpy,win,0,0);

  XFlush(dpy);

  if (COM_CheckParm("-window"))
    putenv("MESA_GLX_FX=window");
  else
    putenv("MESA_GLX_FX=fullscreen");

  ctx = glXCreateContext(dpy,visinfo,NULL,True);

  if (!ctx) {
    fprintf(stderr, "Unable to create glX context.\n");
    exit(1);
  }

  glXMakeCurrent(dpy,win,ctx);

  scr_width = width;
  scr_height = height;

  if (vid.conheight > height)
    vid.conheight = height;
  if (vid.conwidth > width)
    vid.conwidth = width;
  vid.width = vid.conwidth;
  vid.height = vid.conheight;

  vid.aspect = ((float)vid.height / (float)vid.width) *
        (320.0 / 240.0);
  vid.numpages = 2;

  InitSig(); // trap evil signals

  GL_Init();

  sprintf (gldir, "%s/glquake", com_gamedir);
  Sys_mkdir (gldir);

  VID_SetPalette(palette);

  // Check for 3DFX Extensions and initialize them.
  VID_Init8bitPalette();

  Con_SafePrintf ("Video mode %dx%d initialized.\n", width, height);

  vid.recalc_refdef = 1;        // force a surface cache flush
}

void Sys_SendKeyEvents(void)
{
  if (dpy)
  {
    while (Keyboard_Update())
      ;

    while (keyq_head != keyq_tail)
    {
      Key_Event(keyq[keyq_tail].key, keyq[keyq_tail].down);
      keyq_tail = (keyq_tail + 1) & 63;
    }
  }
}

void Force_CenterView_f (void)
{
  cl.viewangles[PITCH] = 0;
}


void IN_Init(void)
{
  Cvar_RegisterVariable (&_windowed_mouse);
  Cvar_RegisterVariable (&m_filter);
  Cvar_RegisterVariable (&mouse_button_commands[0]);
  Cvar_RegisterVariable (&mouse_button_commands[1]);
  Cvar_RegisterVariable (&mouse_button_commands[2]);
  Cmd_AddCommand ("force_centerview", Force_CenterView_f);
}

void IN_Shutdown(void)
{
}

/*
===========
IN_Commands
===========
*/
void IN_Commands (void)
{
  int i;

  for (i=0 ; i<mouse_buttons ; i++) {
    if ( (mouse_buttonstate & (1<<i)) && !(mouse_oldbuttonstate & (1<<i)) )
      Key_Event (K_MOUSE1 + i, true);

    if ( !(mouse_buttonstate & (1<<i)) && (mouse_oldbuttonstate & (1<<i)) )
      Key_Event (K_MOUSE1 + i, false);
  }

  mouse_oldbuttonstate = mouse_buttonstate;
}

/*
===========
IN_Move
===========
*/
void IN_Move (usercmd_t *cmd)
{
  while (Mouse_Update())
    ;

  if (m_filter.value) {
    mouse_x = (mouse_x + old_mouse_x) * 0.5;
    mouse_y = (mouse_y + old_mouse_y) * 0.5;
  }

  old_mouse_x = mouse_x;
  old_mouse_y = mouse_y;
   
  mouse_x *= sensitivity.value;
  mouse_y *= sensitivity.value;
   
  if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) ))
    cmd->sidemove += m_side.value * mouse_x;
  else
    cl.viewangles[YAW] -= m_yaw.value * mouse_x;
  if (in_mlook.state & 1)
    V_StopPitchDrift ();
   
  if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) {
    cl.viewangles[PITCH] += m_pitch.value * mouse_y;
    if (cl.viewangles[PITCH] > 80)
      cl.viewangles[PITCH] = 80;
    if (cl.viewangles[PITCH] < -70)
      cl.viewangles[PITCH] = -70;
  } else {
    if ((in_strafe.state & 1) && noclip_anglehack)
      cmd->upmove -= m_forward.value * mouse_y;
    else
      cmd->forwardmove -= m_forward.value * mouse_y;
  }
  mouse_x = mouse_y = 0.0;
}


void  VID_LockBuffer (void) {}
void  VID_UnlockBuffer (void) {}

