This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/engine/vid_dll.c

600 lines
14 KiB
C

/*
Copyright (C) 1997-2001 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.
*/
// Main windowed and fullscreen graphics interface module. This module
// is used for both the software and OpenGL rendering versions of the
// Quake refresh engine.
#include <windows.h>
#include "engine.h"
#include ".\client\client.h"
// Structure containing functions exported from refresh DLL
renderer_exp_t *re;
extern HWND cl_hwnd;
extern HINSTANCE global_hInstance;
#ifndef WM_MOUSEWHEEL
#define WM_MOUSEWHEEL (WM_MOUSELAST+1) // message that will be supported by the OS
#endif
static UINT MSH_MOUSEWHEEL;
// Console variables that we need to access from this module
cvar_t *vid_gamma;
cvar_t *vid_ref; // Name of Refresh DLL loaded
cvar_t *vid_xpos; // X coordinate of window position
cvar_t *vid_ypos; // Y coordinate of window position
cvar_t *vid_fullscreen;
// Global variables used internally by this module
viddef_t viddef; // global video state; used by other modules
HINSTANCE renderer_dll; // Handle to refresh DLL
bool reflib_active = 0;
HWND cl_hwnd; // Main window handle for life of program
#define VID_NUM_MODES ( sizeof( vid_modes ) / sizeof( vid_modes[0] ) )
LONG WINAPI MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
/*
==========================================================================
DLL GLUE
==========================================================================
*/
void VID_Error (char *fmt, ...)
{
va_list argptr;
char msg[MAX_INPUTLINE];
static bool inupdate;
va_start (argptr, fmt);
vsprintf (msg, fmt, argptr);
va_end (argptr);
Com_Error (ERR_DROP, "%s", msg);
}
stdinout_api_t VID_GetStdio( void )
{
static stdinout_api_t io;
io.api_size = sizeof(stdinout_api_t);
io.print = Sys_Print;
io.printf = Msg;
io.dprintf = MsgDev;
io.error = VID_Error;
io.exit = Com_Quit;
io.input = Sys_ConsoleInput;
return io;
}
//==========================================================================
byte scantokey[128] =
{
// 0 1 2 3 4 5 6 7
// 8 9 A B C D E F
0 , 27, '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '[', ']', 13 , K_CTRL,'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
'\'' , '`', K_SHIFT,'\\', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '/', K_SHIFT,'*',
K_ALT,' ', 0 , K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, 0 , K_HOME,
K_UPARROW,K_PGUP,K_KP_MINUS,K_LEFTARROW,K_KP_5,K_RIGHTARROW, K_KP_PLUS,K_END, //4
K_DOWNARROW,K_PGDN,K_INS,K_DEL,0,0, 0, K_F11,
K_F12,0 , 0 , 0 , 0 , 0 , 0 , 0, // 5
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, // 6
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // 7
};
/*
=======
MapKey
Map from windows to quake keynums
=======
*/
int MapKey (int key)
{
int result;
int modified = ( key >> 16 ) & 255;
bool is_extended = false;
if ( modified > 127)
return 0;
if ( key & ( 1 << 24 ) )
is_extended = true;
result = scantokey[modified];
if ( !is_extended )
{
switch ( result )
{
case K_HOME:
return K_KP_HOME;
case K_UPARROW:
return K_KP_UPARROW;
case K_PGUP:
return K_KP_PGUP;
case K_LEFTARROW:
return K_KP_LEFTARROW;
case K_RIGHTARROW:
return K_KP_RIGHTARROW;
case K_END:
return K_KP_END;
case K_DOWNARROW:
return K_KP_DOWNARROW;
case K_PGDN:
return K_KP_PGDN;
case K_INS:
return K_KP_INS;
case K_DEL:
return K_KP_DEL;
default:
return result;
}
}
else
{
switch ( result )
{
case 0x0D:
return K_KP_ENTER;
case 0x2F:
return K_KP_SLASH;
case 0xAF:
return K_KP_PLUS;
}
return result;
}
}
void AppActivate(bool fActive, bool fMinimize)
{
if(fActive && !fMinimize) host.state = HOST_FRAME;
else if(fMinimize) host.state = HOST_SLEEP;
else host.state = HOST_NOFOCUS;
Key_ClearStates();
// minimize/restore mouse-capture on demand
if(host.state == HOST_FRAME)
{
IN_Activate (true);
S_Activate (true);
}
else
{
IN_Activate (false);
S_Activate (false);
}
}
/*
====================
MainWndProc
main window procedure
====================
*/
LONG WINAPI MainWndProc ( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LONG lRet = 0;
if ( uMsg == MSH_MOUSEWHEEL )
{
if ( ( ( int ) wParam ) > 0 )
{
Key_Event( K_MWHEELUP, true, host.sv_timer );
Key_Event( K_MWHEELUP, false, host.sv_timer );
}
else
{
Key_Event( K_MWHEELDOWN, true, host.sv_timer );
Key_Event( K_MWHEELDOWN, false, host.sv_timer );
}
return DefWindowProc (hWnd, uMsg, wParam, lParam);
}
switch (uMsg)
{
case WM_MOUSEWHEEL:
/*
** this chunk of code theoretically only works under NT4 and Win98
** since this message doesn't exist under Win95
*/
if ( ( short ) HIWORD( wParam ) > 0 )
{
Key_Event( K_MWHEELUP, true, host.sv_timer );
Key_Event( K_MWHEELUP, false, host.sv_timer );
}
else
{
Key_Event( K_MWHEELDOWN, true, host.sv_timer );
Key_Event( K_MWHEELDOWN, false, host.sv_timer );
}
break;
case WM_HOTKEY:
return 0;
case WM_CREATE:
cl_hwnd = hWnd;
MSH_MOUSEWHEEL = RegisterWindowMessage("MSWHEEL_ROLLMSG");
return DefWindowProc (hWnd, uMsg, wParam, lParam);
case WM_PAINT:
SCR_DirtyScreen (); // force entire screen to update next frame
return DefWindowProc (hWnd, uMsg, wParam, lParam);
case WM_DESTROY:
// let sound and input know about this?
cl_hwnd = NULL;
return DefWindowProc (hWnd, uMsg, wParam, lParam);
case WM_ACTIVATE:
{
int fActive, fMinimized;
// KJB: Watch this for problems in fullscreen modes with Alt-tabbing.
fActive = LOWORD(wParam);
fMinimized = (BOOL) HIWORD(wParam);
AppActivate( fActive != WA_INACTIVE, fMinimized);
if ( reflib_active ) re->AppActivate( !( fActive == WA_INACTIVE ) );
}
return DefWindowProc (hWnd, uMsg, wParam, lParam);
case WM_MOVE:
{
int xPos, yPos;
RECT r;
int style;
if (!vid_fullscreen->value)
{
xPos = (short) LOWORD(lParam); // horizontal position
yPos = (short) HIWORD(lParam); // vertical position
r.left = 0;
r.top = 0;
r.right = 1;
r.bottom = 1;
style = GetWindowLong( hWnd, GWL_STYLE );
AdjustWindowRect( &r, style, FALSE );
Cvar_SetValue( "vid_xpos", xPos + r.left);
Cvar_SetValue( "vid_ypos", yPos + r.top);
vid_xpos->modified = false;
vid_ypos->modified = false;
if (host.state == HOST_FRAME)
IN_Activate (true);
}
}
return DefWindowProc (hWnd, uMsg, wParam, lParam);
// this is complicated because Win32 seems to pack multiple mouse events into
// one update sometimes, so we always check all states and look for events
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MOUSEMOVE:
{
int temp;
temp = 0;
if (wParam & MK_LBUTTON)
temp |= 1;
if (wParam & MK_RBUTTON)
temp |= 2;
if (wParam & MK_MBUTTON)
temp |= 4;
IN_MouseEvent (temp);
}
break;
case WM_SYSCOMMAND:
if ( wParam == SC_SCREENSAVE ) return 0;
return DefWindowProc (hWnd, uMsg, wParam, lParam);
case WM_SYSKEYDOWN:
if ( wParam == 13 )
{
if ( vid_fullscreen )
{
Cvar_SetValue( "vid_fullscreen", !vid_fullscreen->value );
}
return 0;
}
// fall through
case WM_KEYDOWN:
Key_Event( MapKey( lParam ), true, host.sv_timer);
break;
case WM_SYSKEYUP:
case WM_KEYUP:
Key_Event( MapKey( lParam ), false, host.sv_timer);
break;
default: // pass all unhandled messages to DefWindowProc
return DefWindowProc (hWnd, uMsg, wParam, lParam);
}
// return 0 if handled message, 1 if not
return DefWindowProc( hWnd, uMsg, wParam, lParam );
}
/*
============
VID_Restart_f
Console command to re-start the video mode and refresh DLL. We do this
simply by setting the modified flag for the vid_ref variable, which will
cause the entire video mode and refresh DLL to be reset on the next frame.
============
*/
void VID_Restart_f (void)
{
vid_ref->modified = true;
}
void VID_Front_f( void )
{
SetWindowLong( cl_hwnd, GWL_EXSTYLE, WS_EX_TOPMOST );
SetForegroundWindow( cl_hwnd );
}
/*
** VID_GetModeInfo
*/
typedef struct vidmode_s
{
const char *description;
int width, height;
int mode;
} vidmode_t;
vidmode_t vid_modes[] =
{
{ "Mode 0: 320x240", 320, 240, 0 },
{ "Mode 1: 640x480", 640, 480, 1 },
{ "Mode 2: 800x600", 800, 600, 2 },
{ "Mode 3: 1024x768", 1024, 768, 3 },
{ "Mode 4: 1280x960", 1280, 960, 4 },
{ "Mode 5: 1280x1024", 1280, 1024, 5 },
{ "Mode 6: 1600x1200", 1600, 1200, 6 },
{ "Mode 7: 2048x1536", 2048, 1536, 7 }
};
bool VID_GetModeInfo( int *width, int *height, int mode )
{
if ( mode < 0 || mode >= VID_NUM_MODES )
return false;
*width = vid_modes[mode].width;
*height = vid_modes[mode].height;
return true;
}
/*
** VID_UpdateWindowPosAndSize
*/
void VID_UpdateWindowPosAndSize( int x, int y )
{
RECT r;
int style;
int w, h;
r.left = 0;
r.top = 0;
r.right = viddef.width;
r.bottom = viddef.height;
style = GetWindowLong( cl_hwnd, GWL_STYLE );
AdjustWindowRect( &r, style, FALSE );
w = r.right - r.left;
h = r.bottom - r.top;
MoveWindow( cl_hwnd, vid_xpos->value, vid_ypos->value, w, h, TRUE );
}
/*
** VID_NewWindow
*/
void VID_NewWindow ( int width, int height)
{
viddef.width = width;
viddef.height = height;
cl.force_refdef = true; // can't use a paused refdef
}
void VID_FreeReflib (void)
{
FreeLibrary( renderer_dll );
memset (&re, 0, sizeof(re));
renderer_dll = NULL;
reflib_active = false;
}
char *FS_Gamedir( void )
{
return GI.gamedir;
}
char *FS_Title( void )
{
return GI.title;
}
/*
==============
VID_InitRenderer
==============
*/
void VID_InitRenderer( void )
{
renderer_imp_t ri;
renderer_t CreateRender;
VID_FreeRenderer();
ri.Fs = pi->Fs;
ri.VFs = pi->VFs;
ri.Mem = pi->Mem;
ri.Script = pi->Script;
ri.Compile = pi->Compile;
ri.Stdio = VID_GetStdio();
ri.Cmd_AddCommand = Cmd_AddCommand;
ri.Cmd_RemoveCommand = Cmd_RemoveCommand;
ri.Cmd_Argc = Cmd_Argc;
ri.Cmd_Argv = Cmd_Argv;
ri.Cmd_ExecuteText = Cbuf_ExecuteText;
ri.gamedir = FS_Gamedir;
ri.title = FS_Title;
ri.Cvar_Get = Cvar_Get;
ri.Cvar_Set = Cvar_Set;
ri.Cvar_SetValue = Cvar_SetValue;
ri.Vid_GetModeInfo = VID_GetModeInfo;
ri.Vid_MenuInit = VID_MenuInit;
ri.Vid_NewWindow = VID_NewWindow;
// studio callbacks
ri.StudioEvent = CL_StudioEvent;
if(( renderer_dll = LoadLibrary( "bin/renderer.dll" )) == 0 )
Sys_Error( "Couldn't load renderer.dll\n" );
if ( ( CreateRender = (void *) GetProcAddress( renderer_dll, "CreateAPI" )) == 0 )
Sys_Error( "CreateInstance: %s has no valid entry point", "renderer.dll" );
re = CreateRender( ri );
if(re->apiversion != RENDERER_API_VERSION)
Sys_Error("mismatch version (%i should be %i)\n", re->apiversion, RENDERER_API_VERSION);
if(re->api_size != sizeof(renderer_exp_t))
Sys_Error("mismatch interface size (%i should be %i)\n", re->api_size, sizeof(renderer_exp_t));
if(!re->Init( global_hInstance, MainWndProc ))
Sys_Error("can't init renderer.dll\n");
reflib_active = true;
}
/*
============
VID_CheckChanges
This function gets called once just before drawing each frame, and it's sole purpose in life
is to check to see if any of the video mode parameters have changed, and if they have to
update the rendering DLL and/or video mode to match.
============
*/
void VID_CheckChanges (void)
{
if ( vid_ref->modified )
{
cl.force_refdef = true; // can't use a paused refdef
S_StopAllSounds();
}
while (vid_ref->modified)
{
/*
** refresh has changed
*/
vid_ref->modified = false;
vid_fullscreen->modified = true;
cl.refresh_prepped = false;
cls.disable_screen = true;
VID_InitRenderer();
cls.disable_screen = false;
}
// update our window position
if ( vid_xpos->modified || vid_ypos->modified )
{
if (!vid_fullscreen->value)
VID_UpdateWindowPosAndSize( vid_xpos->value, vid_ypos->value );
vid_xpos->modified = false;
vid_ypos->modified = false;
}
}
/*
============
VID_Init
============
*/
void VID_Init (void)
{
/* Create the video variables so we know how to start the graphics drivers */
vid_ref = Cvar_Get ("vid_ref", "gl", CVAR_ARCHIVE);
vid_xpos = Cvar_Get ("vid_xpos", "3", CVAR_ARCHIVE);
vid_ypos = Cvar_Get ("vid_ypos", "22", CVAR_ARCHIVE);
vid_fullscreen = Cvar_Get ("vid_fullscreen", "0", CVAR_ARCHIVE);
vid_gamma = Cvar_Get( "vid_gamma", "1", CVAR_ARCHIVE );
/* Add some console commands that we want to handle */
Cmd_AddCommand ("vid_restart", VID_Restart_f);
Cmd_AddCommand ("vid_front", VID_Front_f);
// Start the graphics mode and load refresh DLL
VID_CheckChanges();
}
/*
============
VID_FreeRenderer
============
*/
void VID_FreeRenderer (void)
{
if ( reflib_active )
{
re->Shutdown ();
VID_FreeReflib ();
}
}