1195 lines
28 KiB
C++
1195 lines
28 KiB
C++
#pragma once
|
|
|
|
#include "GraphicsRuntime.h"
|
|
#include "std.h"
|
|
#include "gxruntime.h"
|
|
#include "zmouse.h"
|
|
#include "windows.h"
|
|
|
|
#define SPI_SETMOUSESPEED 113
|
|
|
|
struct gxRuntime::GfxMode {
|
|
DDSURFACEDESC2 desc;
|
|
};
|
|
struct gxRuntime::GfxDriver {
|
|
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;
|
|
|
|
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 = TEXT("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);
|
|
|
|
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;
|
|
LPDIRECTINPUT8 di;
|
|
if (DirectInput8Create(hinst, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&di, NULL) >= 0) {
|
|
//if (DirectInput8Create(hinst, DIRECTINPUT_VERSION, (void**)&di, NULL) >= 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) {
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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 );
|
|
|
|
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();
|
|
}
|
|
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 (unsigned int j = 0; j < d->modes.size(); ++j) delete d->modes[j];
|
|
delete d->guid;
|
|
delete d;
|
|
}
|
|
drivers.clear();
|
|
}
|
|
|
|
int gxRuntime::enumerateGraphicsDrivers() {
|
|
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;
|
|
if (g->d3d_desc.dwDeviceRenderBitDepth) caps |= GFXMODECAPS_3D;
|
|
*name = g->name;
|
|
*c = caps;
|
|
}
|
|
|
|
int gxRuntime::countGraphicsModes(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;
|
|
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;
|
|
*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;
|
|
}
|
|
if (drivers[0]->d3d_desc.dwDeviceRenderBitDepth & bd) caps |= GFXMODECAPS_3D;
|
|
*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") {
|
|
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);
|
|
}
|
|
} 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;
|
|
}
|