/* vid_ggi.c general LibGGI video driver Copyright (C) 1996-1997 Id Software, Inc. Copyright (C) 1999 Marcus Sundberg [mackan@stacken.kth.se] 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: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA $Id$ */ #define _BSD #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include #include #include #include #include "bothdefs.h" // needed by: common.h, net.h, client.h #include "cl_input.h" #include "client.h" // need cls in this file #include "cmd.h" #include "compat.h" #include "console.h" #include "cvar.h" #include "d_local.h" #include "draw.h" #include "host.h" #include "input.h" #include "joystick.h" #include "keys.h" #include "mathlib.h" // needed by: protocol.h, render.h, client.h, modelgen.h, glmodel.h #include "model.h" // needed by: glquake.h #include "net.h" // needed by: client.h #include "protocol.h" // needed by: client.h #include "qargs.h" #include "qendian.h" #include "render.h" // needed by: client.h, gl_model.h, glquake.h #include "sbar.h" #include "sound.h" #include "sys.h" #include "vid.h" #include "view.h" #include "wad.h" extern viddef_t vid; // global video state unsigned short d_8to16table[256]; cvar_t *m_filter; cvar_t *_windowed_mouse; /* Unused */ int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes, VGA_planar; byte *VGA_pagebase; #define NUM_STDBUTTONS 3 #define NUM_BUTTONS 10 static qboolean mouse_avail; static float mouse_x, mouse_y; static float old_mouse_x, old_mouse_y; static int p_mouse_x, p_mouse_y; static float old_windowed_mouse; static ggi_visual_t ggivis = NULL; static ggi_mode mode; static const ggi_directbuffer *dbuf1 = NULL, *dbuf2 = NULL; static uint8 *drawptr = NULL; static void *frameptr[2] = { NULL, NULL }; static void *oneline = NULL; static void *palette = NULL; static int curframe = 0; static int realwidth, realheight; static int doublebuffer; static int scale; static int stride, drawstride; static int pixelsize; static int usedbuf, havedbuf; int VID_options_items = 1; static void do_scale8 (int xsize, int ysize, uint8 * dest, uint8 * src) { int i, j, destinc = stride * 2 - xsize * 2; for (j = 0; j < ysize; j++) { for (i = 0; i < xsize; /* i is incremented below */ ) { register uint32 pix1 = src[i++], pix2 = src[i++]; #ifdef GGI_LITTLE_ENDIAN *((uint32 *) (dest + stride)) = *((uint32 *) dest) = (pix1 | (pix1 << 8) | (pix2 << 16) | (pix2 << 24)); #else *((uint32 *) (dest + stride)) = *((uint32 *) dest) = (pix2 | (pix2 << 8) | (pix1 << 16) | (pix1 << 24)); #endif dest += 4; } dest += destinc; src += xsize; } } static void do_scale16 (int xsize, int ysize, uint8 * dest, uint8 * src) { int i, j, destinc = stride * 2 - xsize * 4; uint16 *palptr = palette; for (j = 0; j < ysize; j++) { for (i = 0; i < xsize; /* i is incremented below */ ) { register uint32 pixel = palptr[src[i++]]; *((uint32 *) (dest + stride)) = *((uint32 *) dest) = pixel | (pixel << 16); dest += 4; } dest += destinc; src += xsize; } } static void do_scale32 (int xsize, int ysize, uint8 * dest, uint8 * src) { int i, j, destinc = stride * 2 - xsize * 8; uint32 *palptr = palette; for (j = 0; j < ysize; j++) { for (i = 0; i < xsize; /* i is incremented below */ ) { register uint32 pixel = palptr[src[i++]]; *((uint32 *) (dest + stride)) = *((uint32 *) (dest)) = pixel; dest += 4; *((uint32 *) (dest + stride)) = *((uint32 *) (dest)) = pixel; dest += 4; } dest += destinc; src += xsize; } } static void do_copy8 (int xsize, int ysize, uint8 * dest, uint8 * src) { int i, j; uint8 *palptr = palette; for (j = 0; j < ysize; j++) { for (i = 0; i < xsize; i++) { dest[i] = palptr[src[i]]; } dest += stride; src += xsize; } } static void do_copy16 (int xsize, int ysize, void *destptr, uint8 * src) { int i, j, destinc = (stride / 2 - xsize) / 2; uint16 *palptr = palette; uint32 *dest = destptr; for (j = 0; j < ysize; j++) { for (i = 0; i < xsize; /* i is incremented below */ ) { register uint32 pixel = palptr[src[i++]]; #ifdef GGI_LITTLE_ENDIAN *(dest++) = pixel | (palptr[src[i++]] << 16); #else *(dest++) = (palptr[src[i++]] << 16) | pixel; #endif } dest += destinc; src += xsize; } } static void do_copy32 (int xsize, int ysize, uint32 * dest, uint8 * src) { int i, j, destinc = stride / 4; uint32 *palptr = palette; for (j = 0; j < ysize; j++) { for (i = 0; i < xsize; i++) { dest[i] = palptr[src[i]]; } dest += destinc; src += xsize; } } void ResetFrameBuffer (void) { int tbuffersize, tcachesize; void *vid_surfcache; // Calculate the sizes we want first tbuffersize = vid.width * vid.height * sizeof (*d_pzbuffer); tcachesize = D_SurfaceCacheForRes (vid.width, vid.height); // Free the old z-buffer if (d_pzbuffer) { free (d_pzbuffer); d_pzbuffer = NULL; } // Free the old surface cache vid_surfcache = D_SurfaceCacheAddress (); if (vid_surfcache) { D_FlushCaches (); free (vid_surfcache); vid_surfcache = NULL; } // Allocate the new z-buffer d_pzbuffer = calloc (tbuffersize, 1); if (!d_pzbuffer) { Sys_Error ("Not enough memory for video mode\n"); } // Allocate the new surface cache; free the z-buffer if we fail vid_surfcache = calloc (tcachesize, 1); if (!vid_surfcache) { free (d_pzbuffer); d_pzbuffer = NULL; Sys_Error ("Not enough memory for video mode\n"); } D_InitCaches (vid_surfcache, tcachesize); } // Called at startup to set up translation tables, takes 256 8 bit RGB values // the palette data will go away after the call, so it must be copied off if // the video driver will need it again void VID_Init (unsigned char *pal) { int pnum; vid.width = GGI_AUTO; vid.height = GGI_AUTO; srandom (getpid ()); if (ggiInit () < 0) { Sys_Error ("VID: Unable to init LibGGI\n"); } ggivis = ggiOpen (NULL); if (!ggivis) { Sys_Error ("VID: Unable to open default visual\n"); } /* Go into async mode */ ggiSetFlags (ggivis, GGIFLAG_ASYNC); VID_GetWindowSize (320, 200); scale = COM_CheckParm ("-scale"); /* specify a LibGGI mode */ if ((pnum = COM_CheckParm ("-ggimode"))) { if (pnum >= com_argc - 1) Sys_Error ("VID: -ggimode \n"); ggiParseMode (com_argv[pnum + 1], &mode); } else { /* This will give the default mode */ ggiParseMode ("", &mode); /* Now put in any parameters given above */ mode.visible.x = vid.width; mode.visible.y = vid.height; } if (scale) { mode.visible.x *= 2; mode.visible.y *= 2; } /* We prefer 8 bit mode unless otherwise specified */ if (mode.graphtype == GT_AUTO) mode.graphtype = GT_8BIT; /* We want double buffering if possible */ if (mode.frames == GGI_AUTO) { ggi_mode tmpmode = mode; tmpmode.frames = 2; if (ggiCheckMode (ggivis, &tmpmode) == 0) { mode = tmpmode; } else { tmpmode.frames = 2; if (ggiCheckMode (ggivis, &tmpmode) == 0) { mode = tmpmode; } } } if (ggiSetMode (ggivis, &mode) != 0) { /* Try again with suggested mode */ if (ggiSetMode (ggivis, &mode) != 0) { Sys_Error ("VID: LibGGI can't set any modes!\n"); } } /* Pixel size must be 1, 2 or 4 bytes */ if (GT_SIZE (mode.graphtype) != 8 && GT_SIZE (mode.graphtype) != 16 && GT_SIZE (mode.graphtype) != 32) { if (GT_SIZE (mode.graphtype) == 24) { Sys_Error ("VID: 24 bits per pixel not supported - try using the palemu target.\n"); } else { Sys_Error ("VID: %d bits per pixel not supported by GGI Quake.\n", GT_SIZE (mode.graphtype)); } } realwidth = mode.visible.x; realheight = mode.visible.y; if (scale) { vid.width = realwidth / 2; vid.height = realheight / 2; } else { vid.width = realwidth; vid.height = realheight; } Con_CheckResize (); // Now that we have a window size, fix console if (mode.frames >= 2) doublebuffer = 1; else doublebuffer = 0; pixelsize = (GT_SIZE (mode.graphtype) + 7) / 8; if (mode.graphtype != GT_8BIT) { if ((palette = malloc (pixelsize * 256)) == NULL) { Sys_Error ("VID: Unable to allocate palette table\n"); } } VID_SetPalette (pal); usedbuf = havedbuf = 0; drawstride = vid.width; stride = realwidth * pixelsize; if ((dbuf1 = ggiDBGetBuffer (ggivis, 0)) != NULL && (dbuf1->type & GGI_DB_SIMPLE_PLB)) { havedbuf = 1; stride = dbuf1->buffer.plb.stride; if (doublebuffer) { if ((dbuf2 = ggiDBGetBuffer (ggivis, 1)) == NULL || !(dbuf2->type & GGI_DB_SIMPLE_PLB)) { /* Only one DB? No double buffering then */ doublebuffer = 0; } } if (doublebuffer) { fprintf (stderr, "VID: Got two DirectBuffers\n"); } else { fprintf (stderr, "VID: Got one DirectBuffer\n"); } if (doublebuffer && !scale && !palette) { usedbuf = 1; drawstride = stride; frameptr[0] = dbuf1->write; if (doublebuffer) { frameptr[1] = dbuf2->write; } else { frameptr[1] = frameptr[0]; } drawptr = frameptr[0]; fprintf (stderr, "VID: Drawing into DirectBuffer\n"); } } if (!usedbuf) { if ((drawptr = malloc (vid.width * vid.height)) == NULL) { Sys_Error ("VID: Unable to allocate draw buffer\n"); } if (!havedbuf && (scale || palette)) { int linesize = pixelsize * realwidth; if (scale) linesize *= 4; if ((oneline = malloc (linesize)) == NULL) { Sys_Error ("VID: Unable to allocate line buffer\n"); } } fprintf (stderr, "VID: Drawing into offscreen memory\n"); } ResetFrameBuffer (); curframe = 0; vid.maxwarpwidth = WARP_WIDTH; vid.maxwarpheight = WARP_HEIGHT; vid.numpages = doublebuffer ? 2 : 1; vid.colormap = host_colormap; vid.buffer = drawptr; vid.rowbytes = drawstride; vid.direct = drawptr; vid.conbuffer = vid.buffer; vid.conrowbytes = vid.rowbytes; vid.conwidth = vid.width; vid.conheight = vid.height; vid.aspect = ((float) vid.height / (float) vid.width) * (320.0 / 240.0); vid.fullbright = 256 - LittleLong (*((int *) vid.colormap + 2048)); } void VID_ShiftPalette (unsigned char *pal) { VID_SetPalette (pal); } void VID_SetPalette (unsigned char *pal) { int i; ggi_color colors[256]; for (i = 0; i < 256; i++) { colors[i].r = pal[i * 3] * 257; colors[i].g = pal[i * 3 + 1] * 257; colors[i].b = pal[i * 3 + 2] * 257; } if (palette) { ggiPackColors (ggivis, palette, colors, 256); } else { ggiSetPalette (ggivis, 0, 256, colors); } } // Called at shutdown void VID_Shutdown (void) { Con_Printf ("VID_Shutdown\n"); if (!usedbuf) { free (drawptr); drawptr = NULL; } if (oneline) { free (oneline); oneline = NULL; } if (palette) { free (palette); palette = NULL; } if (ggivis) { ggiClose (ggivis); ggivis = NULL; } ggiExit (); } // flushes the given rectangles from the view buffer to the screen void VID_Update (vrect_t *rects) { int height = 0; #if 0 // if the window changes dimension, skip this frame if (config_notify) { fprintf (stderr, "config notify\n"); config_notify = 0; vid.width = config_notify_width & ~7; vid.height = config_notify_height; if (doShm) ResetSharedFrameBuffers (); else ResetFrameBuffer (); vid.rowbytes = x_framebuffer[0]->bytes_per_line; vid.buffer = x_framebuffer[curframe]->data; vid.conbuffer = vid.buffer; vid.conwidth = vid.width; vid.conheight = vid.height; vid.conrowbytes = vid.rowbytes; vid.recalc_refdef = 1; // force a surface cache flush Con_CheckResize (); Con_Clear_f (); return; } // force full update if not 8bit if (x_visinfo->depth != 8) { extern int scr_fullupdate; scr_fullupdate = 0; } #endif while (rects) { int y = rects->y + rects->height; if (y > height) height = y; rects = rects->pnext; } if (!usedbuf) { int i; if (havedbuf) { if (ggiResourceAcquire (dbuf1->resource, GGI_ACTYPE_WRITE) != 0 || (doublebuffer ? ggiResourceAcquire (dbuf2->resource, GGI_ACTYPE_WRITE) != 0 : 0)) { ggiPanic ("Unable to acquire DirectBuffer!\n"); } /* ->write is allowed to change at acquire time */ frameptr[0] = dbuf1->write; if (doublebuffer) { frameptr[1] = dbuf2->write; } else { frameptr[1] = frameptr[0]; } } if (scale) { switch (pixelsize) { case 1: if (havedbuf) { do_scale8 (vid.width, height, frameptr[curframe], drawptr); } else { uint8 *buf = drawptr; for (i = 0; i < height; i++) { do_scale8 (vid.width, 1, oneline, buf); ggiPutBox (ggivis, 0, i * 2, realwidth, 2, oneline); buf += vid.width; } } break; case 2: if (havedbuf) { do_scale16 (vid.width, height, frameptr[curframe], drawptr); } else { uint8 *buf = drawptr; for (i = 0; i < height; i++) { do_scale16 (vid.width, 1, oneline, buf); ggiPutBox (ggivis, 0, i * 2, realwidth, 2, oneline); buf += vid.width; } } break; case 4: if (havedbuf) { do_scale32 (vid.width, height, frameptr[curframe], drawptr); } else { uint8 *buf = drawptr; for (i = 0; i < height; i++) { do_scale32 (vid.width, 1, oneline, buf); ggiPutBox (ggivis, 0, i * 2, realwidth, 2, oneline); buf += vid.width; } } break; } } else if (palette) { switch (pixelsize) { case 1: if (havedbuf) { do_copy8 (vid.width, height, frameptr[curframe], drawptr); } else { uint8 *buf = drawptr; for (i = 0; i < height; i++) { do_copy8 (vid.width, 1, oneline, buf); ggiPutBox (ggivis, 0, i, realwidth, 1, oneline); buf += vid.width; } } break; case 2: if (havedbuf) { do_copy16 (vid.width, height, frameptr[curframe], drawptr); } else { uint8 *buf = drawptr; for (i = 0; i < height; i++) { do_copy16 (vid.width, 1, oneline, buf); ggiPutBox (ggivis, 0, i, realwidth, 1, oneline); buf += vid.width; } } break; case 4: if (havedbuf) { do_copy32 (vid.width, height, frameptr[curframe], drawptr); } else { uint8 *buf = drawptr; for (i = 0; i < height; i++) { do_copy32 (vid.width, 1, oneline, buf); ggiPutBox (ggivis, 0, i, realwidth, 1, oneline); buf += vid.width; } } break; } } else { ggiPutBox (ggivis, 0, 0, vid.width, height, drawptr); } if (havedbuf) { ggiResourceRelease (dbuf1->resource); if (doublebuffer) { ggiResourceRelease (dbuf2->resource); } } } if (doublebuffer) { ggiSetDisplayFrame (ggivis, curframe); curframe = !curframe; if (usedbuf) { vid.buffer = vid.conbuffer = vid.direct = drawptr = frameptr[curframe]; } ggiSetWriteFrame (ggivis, curframe); } #if 0 if (GT_SIZE (mode.graphtype) == 16) { do_copy16 (vid.width, height, (uint16 *) frameptr, drawptr); } else if (GT_SIZE (mode.graphtype) == 32) { do_copy32 (vid.width, height, (uint32 *) frameptr, drawptr); } #endif ggiFlush (ggivis); } void D_BeginDirectRect (int x, int y, byte * pbitmap, int width, int height) { // direct drawing of the "accessing disk" icon isn't supported under Linux } void D_EndDirectRect (int x, int y, int width, int height) { // direct drawing of the "accessing disk" icon isn't supported under Linux } /* *************************************************************************** Input handling *************************************************************************** */ static int XLateKey (ggi_key_event * ev) { int key = 0; if (GII_KTYP (ev->label) == GII_KT_DEAD) { ev->label = GII_KVAL (ev->label); } switch (ev->label) { case GIIK_P9: key = KP_PGUP; break; case GIIK_PageUp: key = K_PGUP; break; case GIIK_P3: key = KP_PGDN; break; case GIIK_PageDown: key = K_PGDN; break; case GIIK_P7: key = KP_HOME; break; case GIIK_Home: key = K_HOME; break; case GIIK_P1: key = KP_END; break; case GIIK_End: key = K_END; break; case GIIK_P4: key = KP_LEFTARROW; break; case GIIK_Left: key = K_LEFTARROW; break; case GIIK_P6: key = KP_RIGHTARROW; break; case GIIK_Right: key = K_RIGHTARROW; break; case GIIK_P2: key = KP_DOWNARROW; break; case GIIK_Down: key = K_DOWNARROW; break; case GIIK_P8: key = KP_UPARROW; break; case GIIK_Up: key = K_UPARROW; break; case GIIK_P5: key = KP_5; break; case GIIK_PBegin: key = K_AUX32; break; case GIIK_P0: key = KP_INS; break; case GIIK_Insert: key = K_INS; break; case GIIK_PSeparator: case GIIK_PDecimal: key = KP_DEL; break; case GIIUC_Delete: key = K_DEL; break; case GIIK_PStar: key = KP_MULTIPLY; break; case GIIK_PPlus: key = KP_PLUS; break; case GIIK_PMinus: key = KP_MINUS; break; case GIIK_PSlash: key = KP_DIVIDE; break; case GIIK_PEnter: key = KP_ENTER; break; case GIIUC_Return: key = K_ENTER; break; case GIIUC_Escape: key = K_ESCAPE; break; case GIIUC_Tab: key = K_TAB; break; case GIIK_F1: key = K_F1; break; case GIIK_F2: key = K_F2; break; case GIIK_F3: key = K_F3; break; case GIIK_F4: key = K_F4; break; case GIIK_F5: key = K_F5; break; case GIIK_F6: key = K_F6; break; case GIIK_F7: key = K_F7; break; case GIIK_F8: key = K_F8; break; case GIIK_F9: key = K_F9; break; case GIIK_F10: key = K_F10; break; case GIIK_F11: key = K_F11; break; case GIIK_F12: key = K_F12; break; case GIIUC_BackSpace: key = K_BACKSPACE; break; case GIIK_ShiftL: case GIIK_ShiftR: key = K_SHIFT; break; case GIIK_Execute: case GIIK_CtrlL: case GIIK_CtrlR: key = K_CTRL; break; case GIIK_AltL: case GIIK_MetaL: case GIIK_AltR: case GIIK_MetaR: case GIIK_AltGr: case GIIK_ModeSwitch: key = K_ALT; break; case GIIK_Caps: key = K_CAPSLOCK; break; case GIIK_PrintScreen: key = K_PRNTSCR; break; case GIIK_ScrollLock: key = K_SCRLCK; break; case GIIK_Pause: key = K_PAUSE; break; case GIIK_NumLock: key = KP_NUMLCK; break; case GIIUC_Comma: case GIIUC_Minus: case GIIUC_Period: key = ev->label; break; case GIIUC_Section: key = '~'; break; default: if (ev->label >= 0 && ev->label <= 9) return ev->label; if (ev->label >= 'A' && ev->label <= 'Z') { return ev->label - 'A' + 'a'; } if (ev->label >= 'a' && ev->label <= 'z') return ev->label; if (ev->sym <= 0x7f) { key = ev->sym; if (key >= 'A' && key <= 'Z') { key = key - 'A' + 'a'; } return key; } if (ev->label <= 0x7f) { return ev->label; } break; } return key; } static void GetEvent (void) { ggi_event ev; uint32 b; ggiEventRead (ggivis, &ev, emAll); switch (ev.any.type) { case evKeyPress: Key_Event (XLateKey (&ev.key), 0, true); break; case evKeyRelease: Key_Event (XLateKey (&ev.key), 0, false); break; case evPtrRelative: mouse_x += (float) ev.pmove.x; mouse_y += (float) ev.pmove.y; break; case evPtrAbsolute: mouse_x += (float) (ev.pmove.x - p_mouse_x); mouse_y += (float) (ev.pmove.y - p_mouse_y); p_mouse_x = ev.pmove.x; p_mouse_y = ev.pmove.y; break; case evPtrButtonPress: if (!mouse_avail) return; b = ev.pbutton.button - 1; if (b < NUM_STDBUTTONS) { Key_Event (K_MOUSE1 + b, 0, true); } else if (b < NUM_STDBUTTONS + 2) { b -= 3; if (b) Key_Event (K_MWHEELDOWN, 0, true); else Key_Event (K_MWHEELUP, 0, true); } else if (b < NUM_BUTTONS) { Key_Event (K_AUX32 - NUM_BUTTONS + b, 0, true); } break; case evPtrButtonRelease: if (!mouse_avail) return; b = ev.pbutton.button - 1; if (b < NUM_STDBUTTONS) { Key_Event (K_MOUSE1 + b, 0, false); } else if (b < NUM_STDBUTTONS + 2) { b -= 3; if (b) Key_Event (K_MWHEELDOWN, 0, false); else Key_Event (K_MWHEELUP, 0, false); } else if (b < NUM_BUTTONS) { Key_Event (K_AUX32 - NUM_BUTTONS + b, 0, false); } break; #if 0 case ConfigureNotify: //printf("config notify\n"); config_notify_width = ev.xconfigure.width; config_notify_height = ev.xconfigure.height; config_notify = 1; break; default: #endif } } void IN_SendKeyEvents (void) { /* Get events from LibGGI */ if (ggivis) { struct timeval t = { 0, 0 }; if (ggiEventPoll (ggivis, emAll, &t)) { int i = ggiEventsQueued (ggivis, emAll); while (i--) GetEvent (); } } } void IN_Init (void) { JOY_Init (); old_windowed_mouse = -1; /* Force update */ if (COM_CheckParm ("-nomouse")) return; mouse_x = mouse_y = 0.0; mouse_avail = 1; } void IN_Init_Cvars (void) { JOY_Init_Cvars (); _windowed_mouse = Cvar_Get ("_windowed_mouse", "0", CVAR_ARCHIVE, "Have Quake grab the mouse from X when you play"); m_filter = Cvar_Get ("m_filter", "0", CVAR_ARCHIVE, "Mouse input filtering"); } void IN_Shutdown (void) { JOY_Shutdown (); Con_Printf ("IN_Shutdown\n"); mouse_avail = 0; } void IN_Commands (void) { JOY_Command (); /* Only supported by LibGII 0.7 or later. */ #ifdef GII_CMDCODE_PREFER_RELPTR if (old_windowed_mouse != _windowed_mouse->int_val) { gii_event ev; old_windowed_mouse = _windowed_mouse->int_val; ev.cmd.size = sizeof (gii_cmd_nodata_event); ev.cmd.type = evCommand; ev.cmd.target = GII_EV_TARGET_ALL; ev.cmd.code = _windowed_mouse->int_val ? GII_CMDCODE_PREFER_RELPTR : GII_CMDCODE_PREFER_ABSPTR; ggiEventSend (ggivis, &ev); } #endif } void IN_Move (usercmd_t *cmd) { JOY_Move (cmd); if (!mouse_avail) return; if (m_filter->int_val) { 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->int_val && freelook)) cmd->sidemove += m_side->value * mouse_x; else cl.viewangles[YAW] -= m_yaw->value * mouse_x; if (freelook) V_StopPitchDrift (); if (freelook && !(in_strafe.state & 1)) { cl.viewangles[PITCH] += m_pitch->value * mouse_y; cl.viewangles[PITCH] = bound (-70, cl.viewangles[PITCH], 80); } 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_Init_Cvars (void) { } void VID_LockBuffer (void) { } void VID_UnlockBuffer (void) { } void VID_SetCaption (char *text) { }