Files
BlitzNext/gxruntime/gxruntime.cpp
T
blitz-research 08a613ed0e Initial commit.
2014-01-31 08:23:00 +13:00

1235 lines
28 KiB
C++

#include "std.h"
#include "gxruntime.h"
#include "zmouse.h"
#define SPI_SETMOUSESPEED 113
struct gxRuntime::GfxMode{
DDSURFACEDESC2 desc;
};
struct gxRuntime::GfxDriver{
GUID *guid;
std::string name;
std::vector<GfxMode*> modes;
#ifdef PRO
D3DDEVICEDESC7 d3d_desc;
#endif
};
static const int static_ws=WS_VISIBLE|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX;
static const int scaled_ws=WS_VISIBLE|WS_CAPTION|WS_SYSMENU|WS_SIZEBOX|WS_MINIMIZEBOX|WS_MAXIMIZEBOX;
static string app_title;
static string app_close;
static gxRuntime *runtime;
static bool busy,suspended;
static volatile bool run_flag;
static DDSURFACEDESC2 desktop_desc;
typedef int (_stdcall *LibFunc)( const void *in,int in_sz,void *out,int out_sz );
struct gxDll{
HINSTANCE hinst;
map<string,LibFunc> funcs;
};
static map<string,gxDll*> libs;
static LRESULT CALLBACK windowProc( HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam );
//current gfx mode
//
//0=NONE
//1=SCALED WINDOW
//2=FIXED SIZE WINDOW
//3=EXCLUSIVE
//
static int gfx_mode;
static bool gfx_lost;
static bool auto_suspend;
//for modes 1 and 2
static int mod_cnt;
static MMRESULT timerID;
static IDirectDrawClipper *clipper;
static IDirectDrawSurface7 *primSurf;
static Debugger *debugger;
static set<gxTimer*> timers;
enum{
WM_STOP=WM_USER+1,WM_RUN,WM_END
};
////////////////////
// STATIC STARTUP //
////////////////////
gxRuntime *gxRuntime::openRuntime( HINSTANCE hinst,const string &cmd_line,Debugger *d ){
if( runtime ) return 0;
//create debugger
debugger=d;
//create WNDCLASS
WNDCLASS wndclass;
memset(&wndclass,0,sizeof(wndclass));
wndclass.style=CS_HREDRAW|CS_VREDRAW|CS_OWNDC;
wndclass.lpfnWndProc=::windowProc;
wndclass.hInstance=hinst;
wndclass.lpszClassName="Blitz Runtime Class";
wndclass.hCursor=(HCURSOR)LoadCursor( 0,IDC_ARROW );
wndclass.hbrBackground=(HBRUSH)GetStockObject( BLACK_BRUSH );
RegisterClass( &wndclass );
gfx_mode=0;
clipper=0;primSurf=0;
busy=suspended=false;
run_flag=true;
const char *app_t=" ";
int ws=WS_CAPTION,ws_ex=0;
HWND hwnd=CreateWindowEx( ws_ex,"Blitz Runtime Class",app_t,ws,0,0,0,0,0,0,0,0 );
UpdateWindow( hwnd );
runtime=d_new gxRuntime( hinst,cmd_line,hwnd );
return runtime;
}
void gxRuntime::closeRuntime( gxRuntime *r ){
if( !runtime || runtime!=r ) return;
map<string,gxDll*>::const_iterator it;
for( it=libs.begin();it!=libs.end();++it ){
FreeLibrary( it->second->hinst );
}
libs.clear();
delete runtime;
runtime=0;
}
//////////////////////////
// RUNTIME CONSTRUCTION //
//////////////////////////
typedef int (_stdcall *SetAppCompatDataFunc)( int x,int y );
gxRuntime::gxRuntime( HINSTANCE hi,const string &cl,HWND hw ):
hinst(hi),cmd_line(cl),hwnd(hw),curr_driver(0),enum_all(false),
pointer_visible(true),audio(0),input(0),graphics(0),fileSystem(0),use_di(false){
CoInitialize( 0 );
enumGfx();
TIMECAPS tc;
timeGetDevCaps( &tc,sizeof(tc) );
timeBeginPeriod( tc.wPeriodMin );
memset( &osinfo,0,sizeof(osinfo) );
osinfo.dwOSVersionInfoSize=sizeof(osinfo);
GetVersionEx( &osinfo );
HMODULE ddraw=LoadLibraryA( "ddraw.dll" );
if( ddraw ){
SetAppCompatDataFunc SetAppCompatData=(SetAppCompatDataFunc)GetProcAddress( ddraw,"SetAppCompatData" );
if( SetAppCompatData ) SetAppCompatData( 12,0 );
FreeLibrary( ddraw );
}
}
gxRuntime::~gxRuntime(){
while( timers.size() ) freeTimer( *timers.begin() );
if( audio ) closeAudio( audio );
if( graphics ) closeGraphics( graphics );
if( input ) closeInput( input );
TIMECAPS tc;
timeGetDevCaps( &tc,sizeof(tc) );
timeEndPeriod( tc.wPeriodMin );
denumGfx();
DestroyWindow( hwnd );
UnregisterClass( "Blitz Runtime Class",hinst );
CoUninitialize();
}
void gxRuntime::pauseAudio(){
if( audio ) audio->pause();
}
void gxRuntime::resumeAudio(){
if( audio ) audio->resume();
}
void gxRuntime::backupGraphics(){
if( auto_suspend ){
graphics->backup();
}
}
void gxRuntime::restoreGraphics(){
if( auto_suspend ){
if( !graphics->restore() ) gfx_lost=true;
}
}
void gxRuntime::resetInput(){
if( input ) input->reset();
}
void gxRuntime::acquireInput(){
if( !input ) return;
if( gfx_mode==3 ){
if( use_di ){
use_di=input->acquire();
}else{
}
}
input->reset();
}
void gxRuntime::unacquireInput(){
if( !input ) return;
if( gfx_mode==3 && use_di ) input->unacquire();
input->reset();
}
/////////////
// SUSPEND //
/////////////
void gxRuntime::suspend(){
busy=true;
pauseAudio();
backupGraphics();
unacquireInput();
suspended=true;
busy=false;
if( gfx_mode==3 ) ShowCursor(1);
if( debugger ) debugger->debugStop();
}
////////////
// RESUME //
////////////
void gxRuntime::resume(){
if( gfx_mode==3 ) ShowCursor(0);
busy=true;
acquireInput();
restoreGraphics();
resumeAudio();
suspended=false;
busy=false;
if( debugger ) debugger->debugRun();
}
///////////////////
// FORCE SUSPEND //
///////////////////
void gxRuntime::forceSuspend(){
if( gfx_mode==3 ){
SetForegroundWindow( GetDesktopWindow() );
ShowWindow( GetDesktopWindow(),SW_SHOW );
}else{
suspend();
}
}
//////////////////
// FORCE RESUME //
//////////////////
void gxRuntime::forceResume(){
if( gfx_mode==3 ){
SetForegroundWindow( hwnd );
ShowWindow( hwnd,SW_SHOWMAXIMIZED );
}else{
resume();
}
}
///////////
// PAINT //
///////////
void gxRuntime::paint(){
switch( gfx_mode ){
case 0:
{
}
break;
case 1:case 2: //scaled windowed mode.
{
RECT src,dest;
src.left=src.top=0;
GetClientRect( hwnd,&dest );
src.right=gfx_mode==1 ? graphics->getWidth() : dest.right;
src.bottom=gfx_mode==1 ? graphics->getHeight() : dest.bottom;
POINT p;p.x=p.y=0;ClientToScreen( hwnd,&p );
dest.left+=p.x;dest.right+=p.x;
dest.top+=p.y;dest.bottom+=p.y;
gxCanvas *f=graphics->getFrontCanvas();
primSurf->Blt( &dest,f->getSurface(),&src,0,0 );
}
break;
case 3:
{
//exclusive mode
}
break;
}
}
//////////
// FLIP //
//////////
void gxRuntime::flip( bool vwait ){
gxCanvas *b=graphics->getBackCanvas();
gxCanvas *f=graphics->getFrontCanvas();
int n;
switch( gfx_mode ){
case 1:case 2:
if( vwait ) graphics->vwait();
f->setModify( b->getModify() );
if( f->getModify()!=mod_cnt ){
mod_cnt=f->getModify();
paint();
}
break;
case 3:
if( vwait ){
BOOL vb;
while( graphics->dirDraw->GetVerticalBlankStatus( &vb )>=0 && vb ) {}
n=f->getSurface()->Flip( 0,DDFLIP_WAIT );
}else{
n=f->getSurface()->Flip( 0,DDFLIP_NOVSYNC|DDFLIP_WAIT );
}
if( n>=0 ) return;
string t="Flip Failed! Return code:"+itoa(n&0x7fff);
debugLog( t.c_str() );
break;
}
}
////////////////
// MOVE MOUSE //
////////////////
void gxRuntime::moveMouse( int x,int y ){
POINT p;
RECT rect;
switch( gfx_mode ){
case 1:
GetClientRect( hwnd,&rect );
x=x*(rect.right-rect.left)/graphics->getWidth();
y=y*(rect.bottom-rect.top)/graphics->getHeight();
case 2:
p.x=x;p.y=y;ClientToScreen( hwnd,&p );x=p.x;y=p.y;
break;
case 3:
if( use_di ) return;
break;
default:
return;
}
SetCursorPos( x,y );
}
/////////////////
// WINDOW PROC //
/////////////////
LRESULT gxRuntime::windowProc( HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam ){
if( busy ){
return DefWindowProc( hwnd,msg,wparam,lparam );
}
PAINTSTRUCT ps;
//handle 'special' messages!
switch( msg ){
case WM_PAINT:
if( gfx_mode && !auto_suspend ){
if( !graphics->restore() ) gfx_lost=true;
}
BeginPaint( hwnd,&ps );
paint();
EndPaint( hwnd,&ps );
return DefWindowProc( hwnd,msg,wparam,lparam );
case WM_ERASEBKGND:
return gfx_mode ? 1 : DefWindowProc( hwnd,msg,wparam,lparam );
case WM_CLOSE:
if( app_close.size() ){
int n=MessageBox( hwnd,app_close.c_str(),app_title.c_str(),MB_OKCANCEL|MB_ICONWARNING|MB_SETFOREGROUND|MB_TOPMOST );
if( n!=IDOK ) return 0;
}
asyncEnd();
return 0;
case WM_SETCURSOR:
if( !suspended ){
if( gfx_mode==3 ){
SetCursor( 0 );
return 1;
}else if( !pointer_visible ){
POINT p;
GetCursorPos( &p );
ScreenToClient( hwnd,&p );
RECT r;GetClientRect( hwnd,&r );
if( p.x>=0 && p.y>=0 && p.x<r.right && p.y<r.bottom ){
SetCursor( 0 );
return 1;
}
}
}
break;
case WM_ACTIVATEAPP:
if( auto_suspend ){
if( wparam ){
if( suspended ) resume();
}else{
if( !suspended ) suspend();
}
}
break;
}
if( !input || suspended ) return DefWindowProc( hwnd,msg,wparam,lparam );
if( gfx_mode==3 && use_di ){
use_di=input->acquire();
return DefWindowProc( hwnd,msg,wparam,lparam );
}
static const int MK_ALLBUTTONS=MK_LBUTTON|MK_RBUTTON|MK_MBUTTON;
//handle input messages
switch( msg ){
case WM_LBUTTONDOWN:
input->wm_mousedown(1);
SetCapture(hwnd);
break;
case WM_LBUTTONUP:
input->wm_mouseup(1);
if( !(wparam&MK_ALLBUTTONS) ) ReleaseCapture();
break;
case WM_RBUTTONDOWN:
input->wm_mousedown(2);
SetCapture( hwnd );
break;
case WM_RBUTTONUP:
input->wm_mouseup(2);
if( !(wparam&MK_ALLBUTTONS) ) ReleaseCapture();
break;
case WM_MBUTTONDOWN:
input->wm_mousedown(3);
SetCapture( hwnd );
break;
case WM_MBUTTONUP:
input->wm_mouseup(3);
if( !(wparam&MK_ALLBUTTONS) ) ReleaseCapture();
break;
case WM_MOUSEMOVE:
if( !graphics ) break;
if( gfx_mode==3 && !use_di ){
POINT p;GetCursorPos( &p );
input->wm_mousemove( p.x,p.y );
}else{
int x=(short)(lparam&0xffff),y=lparam>>16;
if( gfx_mode==1 ){
RECT rect;GetClientRect( hwnd,&rect );
x=x*graphics->getWidth()/(rect.right-rect.left);
y=y*graphics->getHeight()/(rect.bottom-rect.top);
}
if( x<0 ) x=0;
else if( x>=graphics->getWidth() ) x=graphics->getWidth()-1;
if( y<0 ) y=0;
else if( y>=graphics->getHeight() ) y=graphics->getHeight()-1;
input->wm_mousemove( x,y );
}
break;
case WM_MOUSEWHEEL:
input->wm_mousewheel( (short)HIWORD( wparam ) );
break;
case WM_KEYDOWN:case WM_SYSKEYDOWN:
if( lparam & 0x40000000 ) break;
if( int n=((lparam>>17)&0x80)|((lparam>>16)&0x7f) ) input->wm_keydown( n );
break;
case WM_KEYUP:case WM_SYSKEYUP:
if( int n=((lparam>>17)&0x80)|((lparam>>16)&0x7f) ) input->wm_keyup( n );
break;
default:
return DefWindowProc( hwnd,msg,wparam,lparam );
}
return 0;
}
static LRESULT CALLBACK windowProc( HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam ){
if( runtime ) return runtime->windowProc( hwnd,msg,wparam,lparam );
return DefWindowProc( hwnd,msg,wparam,lparam );
}
//////////////////////////////
//STOP FROM EXTERNAL SOURCE //
//////////////////////////////
void gxRuntime::asyncStop(){
PostMessage( hwnd,WM_STOP,0,0 );
}
//////////////////////////////
//RUN FROM EXTERNAL SOURCE //
//////////////////////////////
void gxRuntime::asyncRun(){
PostMessage( hwnd,WM_RUN,0,0 );
}
//////////////////////////////
// END FROM EXTERNAL SOURCE //
//////////////////////////////
void gxRuntime::asyncEnd(){
PostMessage( hwnd,WM_END,0,0 );
}
//////////
// IDLE //
//////////
bool gxRuntime::idle(){
for(;;){
MSG msg;
if( suspended && run_flag ){
GetMessage( &msg,0,0,0 );
}else{
if( !PeekMessage( &msg,0,0,0,PM_REMOVE ) ) return run_flag;
}
switch( msg.message ){
case WM_STOP:
if( !suspended ) forceSuspend();
break;
case WM_RUN:
if( suspended ) forceResume();
break;
case WM_END:
debugger=0;
run_flag=false;
break;
default:
DispatchMessage( &msg );
}
}
return run_flag;
}
///////////
// DELAY //
///////////
bool gxRuntime::delay( int ms ){
int t=timeGetTime()+ms;
for(;;){
if( !idle() ) return false;
int d=t-timeGetTime(); //how long left to wait
if( d<=0 ) return true;
if( d>100 ) d=100;
Sleep( d );
}
}
///////////////
// DEBUGSTMT //
///////////////
void gxRuntime::debugStmt( int pos,const char *file ){
if( debugger ) debugger->debugStmt( pos,file );
}
///////////////
// DEBUGSTOP //
///////////////
void gxRuntime::debugStop(){
if( !suspended ) forceSuspend();
}
////////////////
// DEBUGENTER //
////////////////
void gxRuntime::debugEnter( void *frame,void *env,const char *func ){
if( debugger ) debugger->debugEnter( frame,env,func );
}
////////////////
// DEBUGLEAVE //
////////////////
void gxRuntime::debugLeave(){
if( debugger ) debugger->debugLeave();
}
////////////////
// DEBUGERROR //
////////////////
void gxRuntime::debugError( const char *t ){
if( !debugger ) return;
Debugger *d=debugger;
asyncEnd();
if( !suspended ){
forceSuspend();
}
d->debugMsg( t,true );
}
///////////////
// DEBUGINFO //
///////////////
void gxRuntime::debugInfo( const char *t ){
if( !debugger ) return;
Debugger *d=debugger;
asyncEnd();
if( !suspended ){
forceSuspend();
}
d->debugMsg( t,false );
}
//////////////
// DEBUGLOG //
//////////////
void gxRuntime::debugLog( const char *t ){
if( debugger ) debugger->debugLog( t );
}
/////////////////////////
// RETURN COMMAND LINE //
/////////////////////////
string gxRuntime::commandLine(){
return cmd_line;
}
/////////////
// EXECUTE //
/////////////
bool gxRuntime::execute( const string &cmd_line ){
if( !cmd_line.size() ) return false;
//convert cmd_line to cmd and params
string cmd=cmd_line,params;
while( cmd.size() && cmd[0]==' ' ) cmd=cmd.substr( 1 );
if( cmd.find( '\"' )==0 ){
int n=cmd.find( '\"',1 );
if( n!=string::npos ){
params=cmd.substr( n+1 );
cmd=cmd.substr( 1,n-1 );
}
}else{
int n=cmd.find( ' ' );
if( n!=string::npos ){
params=cmd.substr( n+1 );
cmd=cmd.substr( 0,n );
}
}
while( params.size() && params[0]==' ' ) params=params.substr( 1 );
while( params.size() && params[params.size()-1]==' ' ) params=params.substr( 0,params.size()-1 );
SetForegroundWindow( GetDesktopWindow() );
return (int)ShellExecute( GetDesktopWindow(),0,cmd.c_str(),params.size() ? params.c_str() : 0,0,SW_SHOW )>32;
}
///////////////
// APP TITLE //
///////////////
void gxRuntime::setTitle( const string &t,const string &e ){
app_title=t;
app_close=e;
SetWindowText( hwnd,app_title.c_str() );
}
//////////////////
// GETMILLISECS //
//////////////////
int gxRuntime::getMilliSecs(){
return timeGetTime();
}
/////////////////////
// POINTER VISIBLE //
/////////////////////
void gxRuntime::setPointerVisible( bool vis ){
if( pointer_visible==vis ) return;
pointer_visible=vis;
if( gfx_mode==3 ) return;
//force a WM_SETCURSOR
POINT pt;
GetCursorPos( &pt );
SetCursorPos( pt.x,pt.y );
}
/////////////////
// AUDIO SETUP //
/////////////////
gxAudio *gxRuntime::openAudio( int flags ){
if( audio ) return 0;
int f_flags=
FSOUND_INIT_GLOBALFOCUS|
FSOUND_INIT_USEDEFAULTMIDISYNTH;
FSOUND_SetHWND( hwnd );
if( !FSOUND_Init( 44100,1024,f_flags ) ){
return 0;
}
audio=d_new gxAudio( this );
return audio;
}
void gxRuntime::closeAudio( gxAudio *a ){
if( !audio || audio!=a ) return;
delete audio;
audio=0;
}
/////////////////
// INPUT SETUP //
/////////////////
gxInput *gxRuntime::openInput( int flags ){
if( input ) return 0;
IDirectInput7 *di;
if( DirectInputCreateEx( hinst,DIRECTINPUT_VERSION,IID_IDirectInput7,(void**)&di,0 )>=0 ){
input=d_new gxInput( this,di );
acquireInput();
}else{
debugInfo( "Create DirectInput failed" );
}
return input;
}
void gxRuntime::closeInput( gxInput *i ){
if( !input || input!=i ) return;
unacquireInput();
delete input;
input=0;
}
/////////////////////////////////////////////////////
// TIMER CALLBACK FOR AUTOREFRESH OF WINDOWED MODE //
/////////////////////////////////////////////////////
static void CALLBACK timerCallback( UINT id,UINT msg,DWORD user,DWORD dw1,DWORD dw2 ){
if( gfx_mode ){
gxCanvas *f=runtime->graphics->getFrontCanvas();
if( f->getModify()!=mod_cnt ){
mod_cnt=f->getModify();
InvalidateRect( runtime->hwnd,0,false );
}
}
}
////////////////////
// GRAPHICS SETUP //
////////////////////
void gxRuntime::backupWindowState(){
GetWindowRect( hwnd,&t_rect );
t_style=GetWindowLong( hwnd,GWL_STYLE );
}
void gxRuntime::restoreWindowState(){
SetWindowLong( hwnd,GWL_STYLE,t_style );
SetWindowPos(
hwnd,0,t_rect.left,t_rect.top,
t_rect.right-t_rect.left,t_rect.bottom-t_rect.top,
SWP_NOZORDER|SWP_FRAMECHANGED );
}
bool gxRuntime::setDisplayMode( int w,int h,int d,bool d3d,IDirectDraw7 *dirDraw ){
if( d ) return dirDraw->SetDisplayMode( w,h,d,0,0 )>=0;
int best_d=0;
if( d3d ){
#ifdef PRO
int bd=curr_driver->d3d_desc.dwDeviceRenderBitDepth;
if( bd & DDBD_32 ) best_d=32;
else if( bd & DDBD_24 ) best_d=24;
else if( bd & DDBD_16 ) best_d=16;
#endif
}else{
int best_n=0;
for( d=16;d<=32;d+=8 ){
if( dirDraw->SetDisplayMode( w,h,d,0,0 )<0 ) continue;
DDCAPS caps={ sizeof(caps) };
dirDraw->GetCaps( &caps,0 );
int n=0;
if( caps.dwCaps & DDCAPS_BLT ) ++n;
if( caps.dwCaps & DDCAPS_BLTCOLORFILL ) ++n;
if( caps.dwCKeyCaps & DDCKEYCAPS_SRCBLT ) ++n;
if( caps.dwCaps2 & DDCAPS2_WIDESURFACES ) ++n;
if( n==4 ) return true;
if( n>best_n ){
best_d=d;
best_n=n;
}
dirDraw->RestoreDisplayMode();
}
}
return best_d ? dirDraw->SetDisplayMode( w,h,best_d,0,0 )>=0 : false;
}
gxGraphics *gxRuntime::openWindowedGraphics( int w,int h,int d,bool d3d ){
IDirectDraw7 *dd;
if( DirectDrawCreateEx( curr_driver->guid,(void**)&dd,IID_IDirectDraw7,0 )<0 ) return 0;
//set coop level
if( dd->SetCooperativeLevel( hwnd,DDSCL_NORMAL )>=0 ){
//create primary surface
IDirectDrawSurface7 *ps;
DDSURFACEDESC2 desc={sizeof(desc)};
desc.dwFlags=DDSD_CAPS;
desc.ddsCaps.dwCaps=DDSCAPS_PRIMARYSURFACE;
if( dd->CreateSurface( &desc,&ps,0 )>=0 ){
//create clipper
IDirectDrawClipper *cp;
if( dd->CreateClipper( 0,&cp,0 )>=0 ){
//attach clipper
if( ps->SetClipper( cp )>=0 ){
//set clipper HWND
if( cp->SetHWnd( 0,hwnd )>=0 ){
//create front buffer
IDirectDrawSurface7 *fs;
DDSURFACEDESC2 desc={sizeof(desc)};
desc.dwFlags=DDSD_WIDTH|DDSD_HEIGHT|DDSD_CAPS;
desc.dwWidth=w;desc.dwHeight=h;
desc.ddsCaps.dwCaps=DDSCAPS_OFFSCREENPLAIN;
if( d3d ) desc.ddsCaps.dwCaps|=DDSCAPS_3DDEVICE;
if( dd->CreateSurface( &desc,&fs,0 )>=0 ){
if( timerID=timeSetEvent( 100,10,timerCallback,0,TIME_PERIODIC ) ){
//Success!
clipper=cp;
primSurf=ps;
mod_cnt=0;
fs->AddRef();
return d_new gxGraphics( this,dd,fs,fs,d3d );
}
fs->Release();
}
}
}
cp->Release();
}
ps->Release();
}
}
dd->Release();
return 0;
}
gxGraphics *gxRuntime::openExclusiveGraphics( int w,int h,int d,bool d3d ){
IDirectDraw7 *dd;
if( DirectDrawCreateEx( curr_driver->guid,(void**)&dd,IID_IDirectDraw7,0 )<0 ) return 0;
//Set coop level
if( dd->SetCooperativeLevel( hwnd,DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN|DDSCL_ALLOWREBOOT )>=0 ){
//Set display mode
if( setDisplayMode( w,h,d,d3d,dd ) ){
//create primary surface
IDirectDrawSurface7 *ps;
DDSURFACEDESC2 desc={sizeof(desc)};
desc.dwFlags=DDSD_CAPS|DDSD_BACKBUFFERCOUNT;
desc.ddsCaps.dwCaps=DDSCAPS_PRIMARYSURFACE|DDSCAPS_COMPLEX|DDSCAPS_FLIP;
desc.dwBackBufferCount=1;
if( d3d ) desc.ddsCaps.dwCaps|=DDSCAPS_3DDEVICE;
if( dd->CreateSurface( &desc,&ps,0 )>=0 ){
//find back surface
IDirectDrawSurface7 *bs;
DDSCAPS2 caps={sizeof caps};
caps.dwCaps=DDSCAPS_BACKBUFFER;
if( ps->GetAttachedSurface( &caps,&bs )>=0 ){
return d_new gxGraphics( this,dd,ps,bs,d3d );
}
ps->Release();
}
dd->RestoreDisplayMode();
}
}
dd->Release();
return 0;
}
gxGraphics *gxRuntime::openGraphics( int w,int h,int d,int driver,int flags ){
if( graphics ) return 0;
busy=true;
bool d3d=flags & gxGraphics::GRAPHICS_3D ? true : false;
bool windowed=flags & gxGraphics::GRAPHICS_WINDOWED ? true : false;
if( windowed ) driver=0;
curr_driver=drivers[driver];
if( windowed ){
if( graphics=openWindowedGraphics( w,h,d,d3d ) ){
gfx_mode=(flags & gxGraphics::GRAPHICS_SCALED) ? 1 : 2;
auto_suspend=(flags & gxGraphics::GRAPHICS_AUTOSUSPEND) ? true : false;
int ws,ww,hh;
if( gfx_mode==1 ){
ws=scaled_ws;
RECT c_r;
GetClientRect( hwnd,&c_r );
ww=c_r.right-c_r.left;
hh=c_r.bottom-c_r.top;
}else{
ws=static_ws;
ww=w;
hh=h;
}
SetWindowLong( hwnd,GWL_STYLE,ws );
SetWindowPos( hwnd,0,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED );
RECT w_r,c_r;
GetWindowRect( hwnd,&w_r );
GetClientRect( hwnd,&c_r );
int tw=(w_r.right-w_r.left)-(c_r.right-c_r.left);
int th=(w_r.bottom-w_r.top)-(c_r.bottom-c_r.top );
int cx=( GetSystemMetrics( SM_CXSCREEN )-ww )/2;
int cy=( GetSystemMetrics( SM_CYSCREEN )-hh )/2;
POINT zz={0,0};
ClientToScreen( hwnd,&zz );
int bw=zz.x-w_r.left,bh=zz.y-w_r.top;
int wx=cx-bw,wy=cy-bh;if( wy<0 ) wy=0; //not above top!
MoveWindow( hwnd,wx,wy,ww+tw,hh+th,true );
}
}else{
backupWindowState();
SetWindowLong( hwnd,GWL_STYLE,WS_VISIBLE|WS_POPUP );
SetWindowPos( hwnd,0,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED );
ShowCursor( 0 );
if( graphics=openExclusiveGraphics( w,h,d,d3d ) ){
gfx_mode=3;
auto_suspend=true;
SetCursorPos(0,0);
acquireInput();
}else{
ShowCursor( 1 );
restoreWindowState();
}
}
if( !graphics ) curr_driver=0;
gfx_lost=false;
busy=false;
return graphics;
}
void gxRuntime::closeGraphics( gxGraphics *g ){
if( !graphics || graphics!=g ) return;
auto_suspend=false;
busy=true;
unacquireInput();
if( timerID ){ timeKillEvent( timerID );timerID=0; }
if( clipper ){ clipper->Release();clipper=0; }
if( primSurf ){ primSurf->Release();primSurf=0; }
delete graphics;graphics=0;
if( gfx_mode==3 ){
ShowCursor( 1 );
restoreWindowState();
}
gfx_mode=0;
gfx_lost=false;
busy=false;
}
bool gxRuntime::graphicsLost(){
return gfx_lost;
}
gxFileSystem *gxRuntime::openFileSystem( int flags ){
if( fileSystem ) return 0;
fileSystem=d_new gxFileSystem();
return fileSystem;
}
void gxRuntime::closeFileSystem( gxFileSystem *f ){
if( !fileSystem || fileSystem!=f ) return;
delete fileSystem;
fileSystem=0;
}
////////////////////
// GFX ENUM STUFF //
////////////////////
static HRESULT WINAPI enumMode( DDSURFACEDESC2 *desc,void *context ){
int dp=desc->ddpfPixelFormat.dwRGBBitCount;
if( dp==16 || dp==24 || dp==32 ){
gxRuntime::GfxMode *m=d_new gxRuntime::GfxMode;
m->desc=*desc;
gxRuntime::GfxDriver *d=(gxRuntime::GfxDriver*)context;
d->modes.push_back( m );
}
return DDENUMRET_OK;
}
#ifdef PRO
static int maxDevType;
static HRESULT CALLBACK enumDevice( char *desc,char *name,D3DDEVICEDESC7 *devDesc,void *context ){
int t=0;
GUID guid=devDesc->deviceGUID;
if( guid==IID_IDirect3DRGBDevice ) t=1;
else if( guid==IID_IDirect3DHALDevice ) t=2;
else if( guid==IID_IDirect3DTnLHalDevice ) t=3;
if( t>1 && t>maxDevType ){
maxDevType=t;
gxRuntime::GfxDriver *d=(gxRuntime::GfxDriver*)context;
d->d3d_desc=*devDesc;
}
return D3DENUMRET_OK;
}
#endif
static BOOL WINAPI enumDriver( GUID FAR *guid,LPSTR desc,LPSTR name,LPVOID context,HMONITOR hm ){
IDirectDraw7 *dd;
if( DirectDrawCreateEx( guid,(void**)&dd,IID_IDirectDraw7,0 )<0 ) return 0;
if( !guid && !desktop_desc.ddpfPixelFormat.dwRGBBitCount ){
desktop_desc.dwSize=sizeof(desktop_desc);
dd->GetDisplayMode( &desktop_desc );
}
gxRuntime::GfxDriver *d=d_new gxRuntime::GfxDriver;
d->guid=guid ? d_new GUID( *guid ) : 0;
d->name=desc;//string( name )+" "+string( desc );
#ifdef PRO
memset( &d->d3d_desc,0,sizeof(d->d3d_desc) );
IDirect3D7 *dir3d;
if( dd->QueryInterface( IID_IDirect3D7,(void**)&dir3d )>=0 ){
maxDevType=0;
dir3d->EnumDevices( enumDevice,d );
dir3d->Release();
}
#endif
vector<gxRuntime::GfxDriver*> *drivers=(vector<gxRuntime::GfxDriver*>*)context;
drivers->push_back( d );
dd->EnumDisplayModes( 0,0,d,enumMode );
dd->Release();
return 1;
}
void gxRuntime::enumGfx(){
denumGfx();
if( enum_all ){
DirectDrawEnumerateEx( enumDriver,&drivers,DDENUM_ATTACHEDSECONDARYDEVICES|DDENUM_NONDISPLAYDEVICES );
}else{
DirectDrawEnumerateEx( enumDriver,&drivers,0 );
}
}
void gxRuntime::denumGfx(){
for( int k=0;k<drivers.size();++k ){
gxRuntime::GfxDriver *d=drivers[k];
for( int j=0;j<d->modes.size();++j ) delete d->modes[j];
delete d->guid;
delete d;
}
drivers.clear();
}
int gxRuntime::numGraphicsDrivers(){
if( !enum_all ){
enum_all=true;
enumGfx();
}
return drivers.size();
}
void gxRuntime::graphicsDriverInfo( int driver,string *name,int *c ){
GfxDriver *g=drivers[driver];
int caps=0;
#ifdef PRO
if( g->d3d_desc.dwDeviceRenderBitDepth ) caps|=GFXMODECAPS_3D;
#endif
*name=g->name;
*c=caps;
}
int gxRuntime::numGraphicsModes( int driver ){
return drivers[driver]->modes.size();
}
void gxRuntime::graphicsModeInfo( int driver,int mode,int *w,int *h,int *d,int *c ){
GfxDriver *g=drivers[driver];
GfxMode *m=g->modes[mode];
int caps=0;
#ifdef PRO
int bd=0;
switch( m->desc.ddpfPixelFormat.dwRGBBitCount ){
case 16:bd=DDBD_16;break;
case 24:bd=DDBD_24;break;
case 32:bd=DDBD_32;break;
}
if( g->d3d_desc.dwDeviceRenderBitDepth & bd ) caps|=GFXMODECAPS_3D;
#endif
*w=m->desc.dwWidth;
*h=m->desc.dwHeight;
*d=m->desc.ddpfPixelFormat.dwRGBBitCount;
*c=caps;
}
void gxRuntime::windowedModeInfo( int *c ){
int caps=0;
#ifdef PRO
int bd=0;
switch( desktop_desc.ddpfPixelFormat.dwRGBBitCount ){
case 16:bd=DDBD_16;break;
case 24:bd=DDBD_24;break;
case 32:bd=DDBD_32;break;
}
if( drivers[0]->d3d_desc.dwDeviceRenderBitDepth & bd ) caps|=GFXMODECAPS_3D;
#endif
*c=caps;
}
gxTimer *gxRuntime::createTimer( int hertz ){
gxTimer *t=d_new gxTimer( this,hertz );
timers.insert( t );
return t;
}
void gxRuntime::freeTimer( gxTimer *t ){
if( !timers.count( t ) ) return;
timers.erase( t );
delete t;
}
static string toDir( string t ){
if( t.size() && t[t.size()-1]!='\\' ) t+='\\';
return t;
}
string gxRuntime::systemProperty( const std::string &p ){
char buff[MAX_PATH+1];
string t=tolower(p);
if( t=="cpu" ){
return "Intel";
}else if( t=="os" ){
switch( osinfo.dwMajorVersion ){
case 3:
switch( osinfo.dwMinorVersion ){
case 51:return "Windows NT 3.1";
}
break;
case 4:
switch( osinfo.dwMinorVersion ){
case 0:return "Windows 95";
case 10:return "Windows 98";
case 90:return "Windows ME";
}
break;
case 5:
switch( osinfo.dwMinorVersion ){
case 0:return "Windows 2000";
case 1:return "Windows XP";
case 2:return "Windows Server 2003";
}
break;
case 6:
switch( osinfo.dwMinorVersion ){
case 0:return "Windows Vista";
case 1:return "Windows 7";
}
break;
}
}else if( t=="appdir" ){
if( GetModuleFileName( 0,buff,MAX_PATH ) ){
string t=buff;
int n=t.find_last_of( '\\' );
if( n!=string::npos ) t=t.substr( 0,n );
return toDir( t );
}
}else if( t=="apphwnd" ){
return itoa( (int)hwnd );
}else if( t=="apphinstance" ){
return itoa( (int)hinst );
}else if( t=="windowsdir" ){
if( GetWindowsDirectory( buff,MAX_PATH ) ) return toDir( buff );
}else if( t=="systemdir" ){
if( GetSystemDirectory( buff,MAX_PATH ) ) return toDir( buff );
}else if( t=="tempdir" ){
if( GetTempPath( MAX_PATH,buff ) ) return toDir( buff );
}else if( t=="direct3d7" ){
if( graphics ) return itoa( (int)graphics->dir3d );
}else if( t=="direct3ddevice7" ){
if( graphics ) return itoa( (int)graphics->dir3dDev );
}else if( t=="directdraw7" ){
if( graphics ) return itoa( (int)graphics->dirDraw );
}else if( t=="directinput7" ){
if( input ) return itoa( (int)input->dirInput );
}
return "";
}
void gxRuntime::enableDirectInput( bool enable ){
if( use_di=enable ){
acquireInput();
}else{
unacquireInput();
}
}
int gxRuntime::callDll( const std::string &dll,const std::string &func,const void *in,int in_sz,void *out,int out_sz ){
map<string,gxDll*>::const_iterator lib_it=libs.find( dll );
if( lib_it==libs.end() ){
HINSTANCE h=LoadLibrary( dll.c_str() );
if( !h ) return 0;
gxDll *t=d_new gxDll;
t->hinst=h;
lib_it=libs.insert( make_pair( dll,t ) ).first;
}
gxDll *t=lib_it->second;
map<string,LibFunc>::const_iterator fun_it=t->funcs.find( func );
if( fun_it==t->funcs.end() ){
LibFunc f=(LibFunc)GetProcAddress( t->hinst,func.c_str() );
if( !f ) return 0;
fun_it=t->funcs.insert( make_pair( func,f ) ).first;
}
static void *save_esp;
_asm{
mov [save_esp],esp
};
int n=fun_it->second( in,in_sz,out,out_sz );
_asm{
mov esp,[save_esp]
};
return n;
}