Files
BlitzNext/gxruntime/gxruntime.cpp
T

1193 lines
28 KiB
C++
Raw Normal View History

#pragma once
2014-01-31 08:23:00 +13:00
#include "GraphicsRuntime.h"
2014-01-31 08:23:00 +13:00
#include "std.h"
#include "gxruntime.h"
#include "zmouse.h"
#include "windows.h"
2014-01-31 08:23:00 +13:00
struct gxRuntime::GfxMode {
2014-01-31 08:23:00 +13:00
DDSURFACEDESC2 desc;
};
struct gxRuntime::GfxDriver {
2014-01-31 08:23:00 +13:00
GUID *guid;
std::string name;
std::vector<GfxMode*> modes;
D3DDEVICEDESC7 d3d_desc;
};
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;
2014-01-31 08:23:00 +13:00
static string app_title;
static string app_close;
static gxRuntime *runtime;
static bool busy, suspended;
2014-01-31 08:23:00 +13:00
static volatile bool run_flag;
static DDSURFACEDESC2 desktop_desc;
typedef int(_stdcall *LibFunc)(const void *in, int in_sz, void *out, int out_sz);
2014-01-31 08:23:00 +13:00
struct gxDll {
2014-01-31 08:23:00 +13:00
HINSTANCE hinst;
map<string, LibFunc> funcs;
2014-01-31 08:23:00 +13:00
};
static map<string, gxDll*> libs;
2014-01-31 08:23:00 +13:00
static LRESULT CALLBACK windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
2014-01-31 08:23:00 +13:00
//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
2014-01-31 08:23:00 +13:00
};
////////////////////
// STATIC STARTUP //
////////////////////
gxRuntime *gxRuntime::openRuntime(HINSTANCE hinst, const string &cmd_line, Debugger *d) {
if (runtime) return 0;
2014-01-31 08:23:00 +13:00
//create debugger
debugger = d;
2014-01-31 08:23:00 +13:00
//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 = TEXT("Blitz Runtime Class");
wndclass.hCursor = (HCURSOR)LoadCursor(0, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
RegisterClass(&wndclass);
2014-01-31 08:23:00 +13:00
gfx_mode = 0;
clipper = 0; primSurf = 0;
busy = suspended = false;
run_flag = true;
2014-01-31 08:23:00 +13:00
const char *app_t = " ";
int ws = WS_CAPTION, ws_ex = 0;
2014-01-31 08:23:00 +13:00
HWND hwnd = CreateWindowEx(ws_ex, "Blitz Runtime Class", app_t, ws, 0, 0, 0, 0, 0, 0, 0, 0);
2014-01-31 08:23:00 +13:00
UpdateWindow(hwnd);
2014-01-31 08:23:00 +13:00
2016-10-03 17:11:15 +02:00
runtime = new gxRuntime(hinst, cmd_line, hwnd);
2014-01-31 08:23:00 +13:00
return runtime;
}
void gxRuntime::closeRuntime(gxRuntime *r) {
if (!runtime || runtime != r) return;
2014-01-31 08:23:00 +13:00
map<string, gxDll*>::const_iterator it;
for (it = libs.begin(); it != libs.end(); ++it) {
FreeLibrary(it->second->hinst);
2014-01-31 08:23:00 +13:00
}
libs.clear();
delete runtime;
runtime = 0;
2014-01-31 08:23:00 +13:00
}
//////////////////////////
// RUNTIME CONSTRUCTION //
//////////////////////////
typedef int(_stdcall *SetAppCompatDataFunc)(int x, int y);
2014-01-31 08:23:00 +13:00
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) {
2014-01-31 08:23:00 +13:00
CoInitialize(0);
2014-01-31 08:23:00 +13:00
enumGfx();
TIMECAPS tc;
timeGetDevCaps(&tc, sizeof(tc));
timeBeginPeriod(tc.wPeriodMin);
HMODULE ddraw = LoadLibraryA("ddraw.dll");
if (ddraw) {
SetAppCompatDataFunc SetAppCompatData = (SetAppCompatDataFunc)GetProcAddress(ddraw, "SetAppCompatData");
if (SetAppCompatData) SetAppCompatData(12, 0);
FreeLibrary(ddraw);
2014-01-31 08:23:00 +13:00
}
}
gxRuntime::~gxRuntime() {
while (timers.size()) freeTimer(*timers.begin());
if (audio) closeAudio(audio);
if (graphics) closeGraphics(graphics);
if (input) closeInput(input);
2014-01-31 08:23:00 +13:00
TIMECAPS tc;
timeGetDevCaps(&tc, sizeof(tc));
timeEndPeriod(tc.wPeriodMin);
2014-01-31 08:23:00 +13:00
denumGfx();
DestroyWindow(hwnd);
UnregisterClass("Blitz Runtime Class", hinst);
2014-01-31 08:23:00 +13:00
CoUninitialize();
}
void gxRuntime::pauseAudio() {
if (audio) audio->pause();
2014-01-31 08:23:00 +13:00
}
void gxRuntime::resumeAudio() {
if (audio) audio->resume();
2014-01-31 08:23:00 +13:00
}
void gxRuntime::backupGraphics() {
if (auto_suspend) {
2014-01-31 08:23:00 +13:00
graphics->backup();
}
}
void gxRuntime::restoreGraphics() {
if (auto_suspend) {
if (!graphics->restore()) gfx_lost = true;
2014-01-31 08:23:00 +13:00
}
}
void gxRuntime::resetInput() {
if (input) input->reset();
2014-01-31 08:23:00 +13:00
}
void gxRuntime::acquireInput() {
if (!input) return;
if (gfx_mode == 3) {
if (use_di) {
use_di = input->acquire();
} else {
2014-01-31 08:23:00 +13:00
}
}
input->reset();
}
void gxRuntime::unacquireInput() {
if (!input) return;
if (gfx_mode == 3 && use_di) input->unacquire();
2014-01-31 08:23:00 +13:00
input->reset();
}
/////////////
// SUSPEND //
/////////////
void gxRuntime::suspend() {
busy = true;
2014-01-31 08:23:00 +13:00
pauseAudio();
backupGraphics();
unacquireInput();
suspended = true;
busy = false;
2014-01-31 08:23:00 +13:00
if (gfx_mode == 3) ShowCursor(1);
2014-01-31 08:23:00 +13:00
if (debugger) debugger->debugStop();
2014-01-31 08:23:00 +13:00
}
////////////
// RESUME //
////////////
void gxRuntime::resume() {
if (gfx_mode == 3) ShowCursor(0);
busy = true;
2014-01-31 08:23:00 +13:00
acquireInput();
restoreGraphics();
resumeAudio();
suspended = false;
busy = false;
2014-01-31 08:23:00 +13:00
if (debugger) debugger->debugRun();
2014-01-31 08:23:00 +13:00
}
///////////////////
// FORCE SUSPEND //
///////////////////
void gxRuntime::forceSuspend() {
if (gfx_mode == 3) {
SetForegroundWindow(GetDesktopWindow());
ShowWindow(GetDesktopWindow(), SW_SHOW);
} else {
2014-01-31 08:23:00 +13:00
suspend();
}
}
//////////////////
// FORCE RESUME //
//////////////////
void gxRuntime::forceResume() {
if (gfx_mode == 3) {
SetForegroundWindow(hwnd);
ShowWindow(hwnd, SW_SHOWMAXIMIZED);
} else {
2014-01-31 08:23:00 +13:00
resume();
}
}
///////////
// PAINT //
///////////
void gxRuntime::paint() {
switch (gfx_mode) {
case 0:
2014-01-31 08:23:00 +13:00
{
}
break;
case 1:case 2: //scaled windowed mode.
2014-01-31 08:23:00 +13:00
{
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);
2014-01-31 08:23:00 +13:00
}
break;
case 3:
2014-01-31 08:23:00 +13:00
{
//exclusive mode
}
break;
}
}
//////////
// FLIP //
//////////
void gxRuntime::flip(bool vwait) {
gxCanvas *b = graphics->getBackCanvas();
gxCanvas *f = graphics->getFrontCanvas();
2014-01-31 08:23:00 +13:00
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;
2014-01-31 08:23:00 +13:00
}
}
////////////////
// MOVE MOUSE //
////////////////
void gxRuntime::moveMouse(int x, int y) {
2014-01-31 08:23:00 +13:00
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;
2014-01-31 08:23:00 +13:00
}
SetCursorPos(x, y);
2014-01-31 08:23:00 +13:00
}
/////////////////
// WINDOW PROC //
/////////////////
LRESULT gxRuntime::windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
2014-01-31 08:23:00 +13:00
if (busy) {
return DefWindowProc(hwnd, msg, wparam, lparam);
2014-01-31 08:23:00 +13:00
}
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);
2014-01-31 08:23:00 +13:00
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;
}
2014-01-31 08:23:00 +13:00
}
}
break;
case WM_ACTIVATEAPP:
if (auto_suspend) {
if (wparam) {
if (suspended) resume();
} else {
if (!suspended) suspend();
}
2014-01-31 08:23:00 +13:00
}
break;
2014-01-31 08:23:00 +13:00
}
if (!input || suspended) return DefWindowProc(hwnd, msg, wparam, lparam);
2014-01-31 08:23:00 +13:00
if (gfx_mode == 3 && use_di) {
use_di = input->acquire();
return DefWindowProc(hwnd, msg, wparam, lparam);
2014-01-31 08:23:00 +13:00
}
static const int MK_ALLBUTTONS = MK_LBUTTON | MK_RBUTTON | MK_MBUTTON;
2014-01-31 08:23:00 +13:00
//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);
2014-01-31 08:23:00 +13:00
}
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);
2014-01-31 08:23:00 +13:00
}
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);
2014-01-31 08:23:00 +13:00
}
//////////////////////////////
//STOP FROM EXTERNAL SOURCE //
//////////////////////////////
void gxRuntime::asyncStop() {
PostMessage(hwnd, WM_STOP, 0, 0);
2014-01-31 08:23:00 +13:00
}
//////////////////////////////
//RUN FROM EXTERNAL SOURCE //
//////////////////////////////
void gxRuntime::asyncRun() {
PostMessage(hwnd, WM_RUN, 0, 0);
2014-01-31 08:23:00 +13:00
}
//////////////////////////////
// END FROM EXTERNAL SOURCE //
//////////////////////////////
void gxRuntime::asyncEnd() {
PostMessage(hwnd, WM_END, 0, 0);
2014-01-31 08:23:00 +13:00
}
//////////
// IDLE //
//////////
bool gxRuntime::idle() {
for (;;) {
2014-01-31 08:23:00 +13:00
MSG msg;
if (suspended && run_flag) {
GetMessage(&msg, 0, 0, 0);
} else {
if (!PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) return run_flag;
2014-01-31 08:23:00 +13:00
}
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);
2014-01-31 08:23:00 +13:00
}
}
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);
2014-01-31 08:23:00 +13:00
}
}
///////////////
// DEBUGSTMT //
///////////////
void gxRuntime::debugStmt(int pos, const char *file) {
if (debugger) debugger->debugStmt(pos, file);
2014-01-31 08:23:00 +13:00
}
///////////////
// DEBUGSTOP //
///////////////
void gxRuntime::debugStop() {
if (!suspended) forceSuspend();
2014-01-31 08:23:00 +13:00
}
////////////////
// DEBUGENTER //
////////////////
void gxRuntime::debugEnter(void *frame, void *env, const char *func) {
if (debugger) debugger->debugEnter(frame, env, func);
2014-01-31 08:23:00 +13:00
}
////////////////
// DEBUGLEAVE //
////////////////
void gxRuntime::debugLeave() {
if (debugger) debugger->debugLeave();
2014-01-31 08:23:00 +13:00
}
////////////////
// DEBUGERROR //
////////////////
void gxRuntime::debugError(const char *t) {
if (!debugger) return;
Debugger *d = debugger;
2014-01-31 08:23:00 +13:00
asyncEnd();
if (!suspended) {
2014-01-31 08:23:00 +13:00
forceSuspend();
}
d->debugMsg(t, true);
2014-01-31 08:23:00 +13:00
}
///////////////
// DEBUGINFO //
///////////////
void gxRuntime::debugInfo(const char *t) {
if (!debugger) return;
Debugger *d = debugger;
2014-01-31 08:23:00 +13:00
asyncEnd();
if (!suspended) {
2014-01-31 08:23:00 +13:00
forceSuspend();
}
d->debugMsg(t, false);
2014-01-31 08:23:00 +13:00
}
//////////////
// DEBUGLOG //
//////////////
void gxRuntime::debugLog(const char *t) {
if (debugger) debugger->debugLog(t);
2014-01-31 08:23:00 +13:00
}
/////////////////////////
// RETURN COMMAND LINE //
/////////////////////////
string gxRuntime::commandLine() {
2014-01-31 08:23:00 +13:00
return cmd_line;
}
/////////////
// EXECUTE //
/////////////
bool gxRuntime::execute(const string &cmd_line) {
2014-01-31 08:23:00 +13:00
if (!cmd_line.size()) return false;
2014-01-31 08:23:00 +13:00
//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);
2014-01-31 08:23:00 +13:00
}
} else {
int n = cmd.find(' ');
if (n != string::npos) {
params = cmd.substr(n + 1);
cmd = cmd.substr(0, n);
2014-01-31 08:23:00 +13:00
}
}
while (params.size() && params[0] == ' ') params = params.substr(1);
while (params.size() && params[params.size() - 1] == ' ') params = params.substr(0, params.size() - 1);
2014-01-31 08:23:00 +13:00
SetForegroundWindow(GetDesktopWindow());
2014-01-31 08:23:00 +13:00
return (int)ShellExecute(GetDesktopWindow(), 0, cmd.c_str(), params.size() ? params.c_str() : 0, 0, SW_SHOW) > 32;
2014-01-31 08:23:00 +13:00
}
///////////////
// APP TITLE //
///////////////
void gxRuntime::setTitle(const string &t, const string &e) {
app_title = t;
app_close = e;
SetWindowText(hwnd, app_title.c_str());
2014-01-31 08:23:00 +13:00
}
//////////////////
// GETMILLISECS //
//////////////////
int gxRuntime::getMilliSecs() {
2014-01-31 08:23:00 +13:00
return timeGetTime();
}
/////////////////////
// POINTER VISIBLE //
/////////////////////
void gxRuntime::setPointerVisible(bool vis) {
if (pointer_visible == vis) return;
2014-01-31 08:23:00 +13:00
pointer_visible = vis;
if (gfx_mode == 3) return;
2014-01-31 08:23:00 +13:00
//force a WM_SETCURSOR
POINT pt;
GetCursorPos(&pt);
SetCursorPos(pt.x, pt.y);
2014-01-31 08:23:00 +13:00
}
/////////////////
// AUDIO SETUP //
/////////////////
gxAudio *gxRuntime::openAudio(int flags) {
if (audio) return 0;
2014-01-31 08:23:00 +13:00
int f_flags =
FSOUND_INIT_GLOBALFOCUS |
2014-01-31 08:23:00 +13:00
FSOUND_INIT_USEDEFAULTMIDISYNTH;
FSOUND_SetHWND(hwnd);
if (!FSOUND_Init(44100, 1024, f_flags)) {
2014-01-31 08:23:00 +13:00
return 0;
}
2016-10-03 17:11:15 +02:00
audio = new gxAudio(this);
2014-01-31 08:23:00 +13:00
return audio;
}
void gxRuntime::closeAudio(gxAudio *a) {
if (!audio || audio != a) return;
2014-01-31 08:23:00 +13:00
delete audio;
audio = 0;
2014-01-31 08:23:00 +13:00
}
/////////////////
// INPUT SETUP //
/////////////////
gxInput *gxRuntime::openInput(int flags) {
if (input) return 0;
LPDIRECTINPUT8 di;
if (DirectInput8Create(hinst, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&di, NULL) >= 0) {
//if (DirectInput8Create(hinst, DIRECTINPUT_VERSION, (void**)&di, NULL) >= 0) {
2016-10-03 17:11:15 +02:00
input = new gxInput(this, di);
2014-01-31 08:23:00 +13:00
acquireInput();
} else {
debugInfo("Create DirectInput failed");
2014-01-31 08:23:00 +13:00
}
return input;
}
void gxRuntime::closeInput(gxInput *i) {
if (!input || input != i) return;
2014-01-31 08:23:00 +13:00
unacquireInput();
delete input;
input = 0;
2014-01-31 08:23:00 +13:00
}
/////////////////////////////////////////////////////
// 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);
2014-01-31 08:23:00 +13:00
}
}
}
////////////////////
// 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) {
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;
} 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;
2014-01-31 08:23:00 +13:00
}
dirDraw->RestoreDisplayMode();
}
}
return best_d ? dirDraw->SetDisplayMode(w, h, best_d, 0, 0) >= 0 : false;
2014-01-31 08:23:00 +13:00
}
gxGraphics *gxRuntime::openWindowedGraphics(int w, int h, int d, bool d3d) {
2014-01-31 08:23:00 +13:00
IDirectDraw7 *dd;
if (DirectDrawCreateEx(curr_driver->guid, (void**)&dd, IID_IDirectDraw7, 0) < 0) return 0;
2014-01-31 08:23:00 +13:00
//set coop level
if (dd->SetCooperativeLevel(hwnd, DDSCL_NORMAL) >= 0) {
2014-01-31 08:23:00 +13:00
//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) {
2014-01-31 08:23:00 +13:00
//create clipper
IDirectDrawClipper *cp;
if (dd->CreateClipper(0, &cp, 0) >= 0) {
2014-01-31 08:23:00 +13:00
//attach clipper
if (ps->SetClipper(cp) >= 0) {
2014-01-31 08:23:00 +13:00
//set clipper HWND
if (cp->SetHWnd(0, hwnd) >= 0) {
2014-01-31 08:23:00 +13:00
//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;
2014-01-31 08:23:00 +13:00
if (d3d) desc.ddsCaps.dwCaps |= DDSCAPS_3DDEVICE;
2014-01-31 08:23:00 +13:00
if (dd->CreateSurface(&desc, &fs, 0) >= 0) {
if (timerID = timeSetEvent(100, 10, timerCallback, 0, TIME_PERIODIC)) {
2014-01-31 08:23:00 +13:00
//Success!
clipper = cp;
primSurf = ps;
mod_cnt = 0;
2014-01-31 08:23:00 +13:00
fs->AddRef();
2016-10-03 17:11:15 +02:00
return new gxGraphics(this, dd, fs, fs, d3d);
2014-01-31 08:23:00 +13:00
}
fs->Release();
}
}
}
cp->Release();
}
ps->Release();
}
}
dd->Release();
return 0;
}
gxGraphics *gxRuntime::openExclusiveGraphics(int w, int h, int d, bool d3d) {
2014-01-31 08:23:00 +13:00
IDirectDraw7 *dd;
if (DirectDrawCreateEx(curr_driver->guid, (void**)&dd, IID_IDirectDraw7, 0) < 0) return 0;
2014-01-31 08:23:00 +13:00
//Set coop level
if (dd->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWREBOOT) >= 0) {
2014-01-31 08:23:00 +13:00
//Set display mode
if (setDisplayMode(w, h, d, d3d, dd)) {
2014-01-31 08:23:00 +13:00
//create primary surface
IDirectDrawSurface7 *ps;
DDSURFACEDESC2 desc = { sizeof(desc) };
desc.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP;
2014-01-31 08:23:00 +13:00
desc.dwBackBufferCount = 1;
if (d3d) desc.ddsCaps.dwCaps |= DDSCAPS_3DDEVICE;
2014-01-31 08:23:00 +13:00
if (dd->CreateSurface(&desc, &ps, 0) >= 0) {
2014-01-31 08:23:00 +13:00
//find back surface
IDirectDrawSurface7 *bs;
DDSCAPS2 caps = { sizeof caps };
caps.dwCaps = DDSCAPS_BACKBUFFER;
if (ps->GetAttachedSurface(&caps, &bs) >= 0) {
2016-10-03 17:11:15 +02:00
return new gxGraphics(this, dd, ps, bs, d3d);
2014-01-31 08:23:00 +13:00
}
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;
2014-01-31 08:23:00 +13:00
busy = true;
2014-01-31 08:23:00 +13:00
bool d3d = flags & gxGraphics::GRAPHICS_3D ? true : false;
bool windowed = flags & gxGraphics::GRAPHICS_WINDOWED ? true : false;
2014-01-31 08:23:00 +13:00
if (windowed) driver = 0;
2014-01-31 08:23:00 +13:00
curr_driver = drivers[driver];
2014-01-31 08:23:00 +13:00
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;
2014-01-31 08:23:00 +13:00
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;
2014-01-31 08:23:00 +13:00
}
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);
2014-01-31 08:23:00 +13:00
}
} else {
2014-01-31 08:23:00 +13:00
backupWindowState();
SetWindowLong(hwnd, GWL_STYLE, WS_VISIBLE | WS_POPUP);
SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
2014-01-31 08:23:00 +13:00
ShowCursor(0);
if (graphics = openExclusiveGraphics(w, h, d, d3d)) {
gfx_mode = 3;
auto_suspend = true;
SetCursorPos(0, 0);
2014-01-31 08:23:00 +13:00
acquireInput();
} else {
ShowCursor(1);
2014-01-31 08:23:00 +13:00
restoreWindowState();
}
}
if (!graphics) curr_driver = 0;
2014-01-31 08:23:00 +13:00
gfx_lost = false;
2014-01-31 08:23:00 +13:00
busy = false;
2014-01-31 08:23:00 +13:00
return graphics;
}
void gxRuntime::closeGraphics(gxGraphics *g) {
if (!graphics || graphics != g) return;
2014-01-31 08:23:00 +13:00
auto_suspend = false;
2014-01-31 08:23:00 +13:00
busy = true;
2014-01-31 08:23:00 +13:00
unacquireInput();
if (timerID) { timeKillEvent(timerID); timerID = 0; }
if (clipper) { clipper->Release(); clipper = 0; }
if (primSurf) { primSurf->Release(); primSurf = 0; }
delete graphics; graphics = 0;
2014-01-31 08:23:00 +13:00
if (gfx_mode == 3) {
ShowCursor(1);
2014-01-31 08:23:00 +13:00
restoreWindowState();
}
gfx_mode = 0;
2014-01-31 08:23:00 +13:00
gfx_lost = false;
2014-01-31 08:23:00 +13:00
busy = false;
2014-01-31 08:23:00 +13:00
}
bool gxRuntime::graphicsLost() {
2014-01-31 08:23:00 +13:00
return gfx_lost;
}
gxFileSystem *gxRuntime::openFileSystem(int flags) {
if (fileSystem) return 0;
2014-01-31 08:23:00 +13:00
2016-10-03 17:11:15 +02:00
fileSystem = new gxFileSystem();
2014-01-31 08:23:00 +13:00
return fileSystem;
}
void gxRuntime::closeFileSystem(gxFileSystem *f) {
if (!fileSystem || fileSystem != f) return;
2014-01-31 08:23:00 +13:00
delete fileSystem;
fileSystem = 0;
2014-01-31 08:23:00 +13:00
}
////////////////////
// GFX ENUM STUFF //
////////////////////
static HRESULT WINAPI enumMode(DDSURFACEDESC2 *desc, void *context) {
int dp = desc->ddpfPixelFormat.dwRGBBitCount;
if (dp == 16 || dp == 24 || dp == 32) {
2016-10-03 17:11:15 +02:00
gxRuntime::GfxMode *m = new gxRuntime::GfxMode;
m->desc = *desc;
gxRuntime::GfxDriver *d = (gxRuntime::GfxDriver*)context;
d->modes.push_back(m);
2014-01-31 08:23:00 +13:00
}
return DDENUMRET_OK;
}
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;
2014-01-31 08:23:00 +13:00
}
return D3DENUMRET_OK;
}
static BOOL WINAPI enumDriver(GUID FAR *guid, LPSTR desc, LPSTR name, LPVOID context, HMONITOR hm) {
2014-01-31 08:23:00 +13:00
IDirectDraw7 *dd;
if (DirectDrawCreateEx(guid, (void**)&dd, IID_IDirectDraw7, 0) < 0) return 0;
2014-01-31 08:23:00 +13:00
if (!guid && !desktop_desc.ddpfPixelFormat.dwRGBBitCount) {
desktop_desc.dwSize = sizeof(desktop_desc);
dd->GetDisplayMode(&desktop_desc);
2014-01-31 08:23:00 +13:00
}
2016-10-03 17:11:15 +02:00
gxRuntime::GfxDriver *d = new gxRuntime::GfxDriver;
2014-01-31 08:23:00 +13:00
2016-10-03 17:11:15 +02:00
d->guid = guid ? new GUID(*guid) : 0;
d->name = desc;//string( name )+" "+string( desc );
2014-01-31 08:23:00 +13:00
memset(&d->d3d_desc, 0, sizeof(d->d3d_desc));
2014-01-31 08:23:00 +13:00
IDirect3D7 *dir3d;
if (dd->QueryInterface(IID_IDirect3D7, (void**)&dir3d) >= 0) {
maxDevType = 0;
dir3d->EnumDevices(enumDevice, d);
2014-01-31 08:23:00 +13:00
dir3d->Release();
}
vector<gxRuntime::GfxDriver*> *drivers = (vector<gxRuntime::GfxDriver*>*)context;
drivers->push_back(d);
dd->EnumDisplayModes(0, 0, d, enumMode);
2014-01-31 08:23:00 +13:00
dd->Release();
return 1;
}
void gxRuntime::enumGfx() {
2014-01-31 08:23:00 +13:00
denumGfx();
if (enum_all) {
DirectDrawEnumerateEx(enumDriver, &drivers, DDENUM_ATTACHEDSECONDARYDEVICES | DDENUM_NONDISPLAYDEVICES);
} else {
DirectDrawEnumerateEx(enumDriver, &drivers, 0);
2014-01-31 08:23:00 +13:00
}
}
void gxRuntime::denumGfx() {
for (size_t k = 0; k < drivers.size(); ++k) {
gxRuntime::GfxDriver *d = drivers[k];
for (size_t j = 0; j < d->modes.size(); ++j) delete d->modes[j];
2014-01-31 08:23:00 +13:00
delete d->guid;
delete d;
}
drivers.clear();
}
int gxRuntime::enumerateGraphicsDrivers() {
if (!enum_all) {
enum_all = true;
2014-01-31 08:23:00 +13:00
enumGfx();
}
return drivers.size();
}
void gxRuntime::graphicsDriverInfo(int driver, string *name, int *c) {
GfxDriver *g = drivers[driver];
int caps = 0;
if (g->d3d_desc.dwDeviceRenderBitDepth) caps |= GFXMODECAPS_3D;
*name = g->name;
*c = caps;
2014-01-31 08:23:00 +13:00
}
int gxRuntime::countGraphicsModes(int driver) {
2014-01-31 08:23:00 +13:00
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;
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;
2014-01-31 08:23:00 +13:00
}
if (g->d3d_desc.dwDeviceRenderBitDepth & bd) caps |= GFXMODECAPS_3D;
*w = m->desc.dwWidth;
*h = m->desc.dwHeight;
*d = m->desc.ddpfPixelFormat.dwRGBBitCount;
*c = caps;
}
void gxRuntime::windowedModeInfo(int *c) {
int caps = 0;
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;
2014-01-31 08:23:00 +13:00
}
if (drivers[0]->d3d_desc.dwDeviceRenderBitDepth & bd) caps |= GFXMODECAPS_3D;
*c = caps;
2014-01-31 08:23:00 +13:00
}
gxTimer *gxRuntime::createTimer(int hertz) {
2016-10-03 17:11:15 +02:00
gxTimer *t = new gxTimer(this, hertz);
timers.insert(t);
2014-01-31 08:23:00 +13:00
return t;
}
void gxRuntime::freeTimer(gxTimer *t) {
if (!timers.count(t)) return;
timers.erase(t);
2014-01-31 08:23:00 +13:00
delete t;
}
static string toDir(string t) {
if (t.size() && t[t.size() - 1] != '\\') t += '\\';
2014-01-31 08:23:00 +13:00
return t;
}
string gxRuntime::systemProperty(const std::string &p) {
char buff[MAX_PATH + 1];
string t = tolower(p);
if (t == "cpu") {
2014-01-31 08:23:00 +13:00
return "Intel";
} else if (t == "os") {
return "Windows";
} 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);
2014-01-31 08:23:00 +13:00
}
} 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);
2014-01-31 08:23:00 +13:00
}
return "";
}
void gxRuntime::enableDirectInput(bool enable) {
if (use_di = enable) {
2014-01-31 08:23:00 +13:00
acquireInput();
} else {
2014-01-31 08:23:00 +13:00
unacquireInput();
}
}
int gxRuntime::callDll(const std::string &dll, const std::string &func, const void *in, int in_sz, void *out, int out_sz) {
2014-01-31 08:23:00 +13:00
map<string, gxDll*>::const_iterator lib_it = libs.find(dll);
2014-01-31 08:23:00 +13:00
if (lib_it == libs.end()) {
HINSTANCE h = LoadLibrary(dll.c_str());
if (!h) return 0;
2016-10-03 17:11:15 +02:00
gxDll *t = new gxDll;
t->hinst = h;
lib_it = libs.insert(make_pair(dll, t)).first;
2014-01-31 08:23:00 +13:00
}
gxDll *t = lib_it->second;
map<string, LibFunc>::const_iterator fun_it = t->funcs.find(func);
2014-01-31 08:23:00 +13:00
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;
2014-01-31 08:23:00 +13:00
}
static void *save_esp;
_asm {
mov[save_esp], esp
2014-01-31 08:23:00 +13:00
};
int n = fun_it->second(in, in_sz, out, out_sz);
2014-01-31 08:23:00 +13:00
_asm {
mov esp, [save_esp]
2014-01-31 08:23:00 +13:00
};
return n;
}