Initial commit
This commit is contained in:
74
engines/bagel/mfc/libs/array.h
Normal file
74
engines/bagel/mfc/libs/array.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BAGEL_MFC_LIBS_ARRAY_H
|
||||
#define BAGEL_MFC_LIBS_ARRAY_H
|
||||
|
||||
#include "common/array.h"
|
||||
|
||||
namespace Bagel {
|
||||
namespace MFC {
|
||||
namespace Libs {
|
||||
|
||||
template<class T>
|
||||
class Array : public Common::Array<T> {
|
||||
public:
|
||||
constexpr Array() : Common::Array<T>() {
|
||||
}
|
||||
explicit Array(size_t count) : Common::Array<T>(count) {
|
||||
}
|
||||
Array(size_t count, const T &value) :
|
||||
Common::Array<T>(count, value) {
|
||||
}
|
||||
Array(const Common::Array<T> &array) : Common::Array<T>(array) {
|
||||
}
|
||||
Array(Common::Array<T> &&old) : Common::Array<T>(old) {
|
||||
}
|
||||
Array(std::initializer_list<T> list) : Common::Array<T>(list) {
|
||||
}
|
||||
template<class T2>
|
||||
Array(const T2 *array, size_t n) :
|
||||
Common::Array<T>(array, n) {
|
||||
}
|
||||
|
||||
int indexOf(const T &value) const {
|
||||
for (int i = 0; i < (int)this->size(); ++i) {
|
||||
if (this->operator[](i) == value)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
bool remove(const T &value) {
|
||||
int idx = this->indexOf(value);
|
||||
if (idx != -1)
|
||||
this->remove_at(idx);
|
||||
return idx != -1;
|
||||
}
|
||||
bool contains(const T &value) const {
|
||||
return this->indexOf(value) != -1;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Libs
|
||||
} // namespace MFC
|
||||
} // namespace Bagel
|
||||
|
||||
#endif
|
||||
551
engines/bagel/mfc/libs/event_loop.cpp
Normal file
551
engines/bagel/mfc/libs/event_loop.cpp
Normal file
@@ -0,0 +1,551 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/system.h"
|
||||
#include "engines/engine.h"
|
||||
#include "bagel/mfc/afxwin.h"
|
||||
#include "bagel/mfc/libs/event_loop.h"
|
||||
#include "bagel/mfc/winnt.h"
|
||||
|
||||
namespace Bagel {
|
||||
namespace MFC {
|
||||
namespace Libs {
|
||||
|
||||
#define FRAME_RATE 50
|
||||
|
||||
void EventLoop::runEventLoop(bool isModalDialog) {
|
||||
MSG msg;
|
||||
|
||||
while (!shouldQuit() && !_activeWindows.empty()) {
|
||||
CWnd *activeWin = GetActiveWindow();
|
||||
if (activeWin->_modalResult != DEFAULT_MODAL_RESULT)
|
||||
break;
|
||||
|
||||
if (!GetMessage(msg))
|
||||
break;
|
||||
|
||||
CWnd *mainWnd = GetActiveWindow();
|
||||
if (msg.message != WM_NULL && mainWnd && !mainWnd->PreTranslateMessage(&msg) &&
|
||||
(!isModalDialog || !mainWnd->IsDialogMessage(&msg))) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EventLoop::SetActiveWindow(CWnd *wnd) {
|
||||
assert(!shouldQuit());
|
||||
if (wnd == GetActiveWindow())
|
||||
// Already the active window
|
||||
return;
|
||||
|
||||
// If it's the first window added, and we don't have
|
||||
// a main window defined, set it as the main window
|
||||
if (_activeWindows.empty())
|
||||
_mainWindow = wnd;
|
||||
|
||||
// Add the window to the list
|
||||
// Note: Currently we don't supportly multiple
|
||||
// open windows at the same time. Each new window
|
||||
// is effectively a dialog on top of previous ones
|
||||
|
||||
if (!_activeWindows.empty()) {
|
||||
auto *win = _activeWindows.top();
|
||||
win->SendMessage(WM_ACTIVATE, MAKEWPARAM(WA_INACTIVE, 0), 0);
|
||||
win->SendMessage(WM_PALETTECHANGED, (WPARAM)wnd);
|
||||
}
|
||||
|
||||
_activeWindows.push(wnd);
|
||||
wnd->SendMessage(WM_ACTIVATE, MAKEWPARAM(WA_ACTIVE, 0), 0);
|
||||
wnd->SendMessage(WM_QUERYNEWPALETTE, 0, 0);
|
||||
}
|
||||
|
||||
void EventLoop::PopActiveWindow() {
|
||||
_activeWindows.top()->SendMessage(WM_ACTIVATE, MAKEWPARAM(WA_INACTIVE, 0), 0);
|
||||
_activeWindows.pop();
|
||||
|
||||
if (!_activeWindows.empty()) {
|
||||
CWnd *wnd = _activeWindows.top();
|
||||
wnd->RedrawWindow(nullptr, nullptr, RDW_INVALIDATE | RDW_ALLCHILDREN);
|
||||
wnd->SendMessage(WM_ACTIVATE, MAKEWPARAM(WA_ACTIVE, 0), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void EventLoop::doModal(CWnd *wnd) {
|
||||
SetActiveWindow(wnd);
|
||||
runEventLoop(true);
|
||||
if (GetActiveWindow() == wnd)
|
||||
wnd->DestroyWindow();
|
||||
}
|
||||
|
||||
void EventLoop::checkMessages() {
|
||||
// Don't do any actual ScummVM event handling
|
||||
// until at least one window has been set up
|
||||
if (_activeWindows.empty())
|
||||
return;
|
||||
|
||||
if (_messages.empty() && _idleCtr >= 0) {
|
||||
if (!OnIdle(_idleCtr))
|
||||
// OnIdle returning false means disabling permanently
|
||||
_idleCtr = -1;
|
||||
}
|
||||
|
||||
// Poll for event in ScummVM event manager
|
||||
MSG priorMsg;
|
||||
Libs::Event ev;
|
||||
|
||||
while (pollEvents(ev)) {
|
||||
// Handle custom keybinding actions mapping back to keys
|
||||
if (_keybindProc) {
|
||||
if (ev.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START) {
|
||||
ev.type = Common::EVENT_KEYDOWN;
|
||||
ev.kbd.keycode = _keybindProc(ev.customType);
|
||||
} else if (ev.type == Common::EVENT_CUSTOM_ENGINE_ACTION_END) {
|
||||
ev.type = Common::EVENT_KEYUP;
|
||||
ev.kbd.keycode = _keybindProc(ev.customType);
|
||||
}
|
||||
}
|
||||
|
||||
HWND hWnd = nullptr;
|
||||
setMessageWnd(ev, hWnd);
|
||||
MSG msg = ev;
|
||||
msg.hwnd = hWnd;
|
||||
|
||||
if (msg.message == WM_MOUSEMOVE &&
|
||||
priorMsg.message == WM_MOUSEMOVE) {
|
||||
// Preventing multiple sequential mouse move messages
|
||||
priorMsg = msg;
|
||||
} else if (msg.message != WM_NULL) {
|
||||
if (priorMsg.message != WM_NULL)
|
||||
_messages.push(priorMsg);
|
||||
priorMsg = msg;
|
||||
}
|
||||
|
||||
if (msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST) {
|
||||
// Update saved mouse position
|
||||
_mousePos.x = LOWORD(msg.lParam);
|
||||
_mousePos.y = HIWORD(msg.lParam);
|
||||
|
||||
// For mouse messages, if the highlighted control
|
||||
// changes, generate a WM_SETCURSOR event
|
||||
if (msg.hwnd != _highlightedWin) {
|
||||
// Add mouse leave event if win is still alive
|
||||
CWnd *highlightedWin = CWnd::FromHandle(_highlightedWin);
|
||||
if (highlightedWin)
|
||||
highlightedWin->PostMessage(WM_MOUSELEAVE);
|
||||
|
||||
// Switch to newly highlighted control
|
||||
_highlightedWin = msg.hwnd;
|
||||
if (_highlightedWin)
|
||||
PostMessage(_highlightedWin,
|
||||
WM_SETCURSOR, (WPARAM)msg.hwnd,
|
||||
MAKELPARAM(HTCLIENT, msg.message)
|
||||
);
|
||||
}
|
||||
} else if (msg.message == WM_QUIT) {
|
||||
// Add a window message close message as well
|
||||
MSG cmsg;
|
||||
cmsg.message = WM_CLOSE;
|
||||
cmsg.hwnd = hWnd;
|
||||
_messages.push(cmsg);
|
||||
}
|
||||
}
|
||||
|
||||
if (priorMsg.message != WM_NULL)
|
||||
_messages.push(priorMsg);
|
||||
|
||||
// If there are no pending messages,
|
||||
// do a brief pause and check for frame updates
|
||||
if (_messages.empty())
|
||||
checkForFrameUpdate();
|
||||
}
|
||||
|
||||
bool EventLoop::GetMessage(MSG &msg) {
|
||||
checkMessages();
|
||||
|
||||
// Queue window/control repaints if needed
|
||||
for (CWnd *wnd : _updateWnds) {
|
||||
if (wnd->IsWindowDirty())
|
||||
wnd->PostMessage(WM_PAINT);
|
||||
}
|
||||
|
||||
_updateWnds.clear();
|
||||
|
||||
// Check for any existing messages
|
||||
if (!_messages.empty()) {
|
||||
msg = _messages.pop();
|
||||
|
||||
if (msg.hwnd) {
|
||||
if ((msg.message == WM_KEYDOWN || msg.message == WM_KEYUP) &&
|
||||
!_kbdHookProc.empty()) {
|
||||
if (_kbdHookProc.front()(HC_ACTION, msg.wParam, msg.lParam))
|
||||
msg.message = WM_NULL;
|
||||
}
|
||||
} else if (msg.message != WM_QUIT) {
|
||||
msg.message = WM_NULL;
|
||||
} else {
|
||||
debug(1, "Got WM_QUIT message..");
|
||||
}
|
||||
} else {
|
||||
msg.message = WM_NULL;
|
||||
}
|
||||
|
||||
return !((msg.message == WM_QUIT) || (shouldQuit() && _messages.empty()));
|
||||
}
|
||||
|
||||
void EventLoop::setMessageWnd(Common::Event &ev, HWND &hWnd) {
|
||||
if (isMouseMsg(ev)) {
|
||||
setMouseMessageWnd(ev, hWnd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_focusedWin && (ev.type == Common::EVENT_KEYDOWN ||
|
||||
ev.type == Common::EVENT_KEYUP)) {
|
||||
hWnd = _focusedWin->m_hWnd;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isJoystickMsg(ev)) {
|
||||
CWnd *joystickWin = CWnd::FromHandle(_joystickWin);
|
||||
|
||||
if (joystickWin) {
|
||||
switch (ev.type) {
|
||||
case Common::EVENT_JOYAXIS_MOTION:
|
||||
if (ev.joystick.axis == 0)
|
||||
_joystickPos.x = ev.joystick.position + JOYSTICK_REST_POS;
|
||||
else
|
||||
_joystickPos.y = ev.joystick.position + JOYSTICK_REST_POS;
|
||||
|
||||
joystickWin->SendMessage(MM_JOY1MOVE, JOYSTICKID1,
|
||||
MAKELPARAM(_joystickPos.x, _joystickPos.y));
|
||||
break;
|
||||
|
||||
default:
|
||||
_joystickButtons = ev.joystick.button;
|
||||
_joystickWin->SendMessage(MM_JOY1MOVE, JOYSTICKID1,
|
||||
MAKELPARAM(_joystickPos.x, _joystickPos.y));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hWnd = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback, send message to active window
|
||||
CWnd *activeWin = _activeWindows.top();
|
||||
hWnd = activeWin->m_hWnd;
|
||||
}
|
||||
|
||||
void EventLoop::setMouseMessageWnd(Common::Event &ev, HWND &hWnd) {
|
||||
// Handle mouse capture
|
||||
if (_captureWin) {
|
||||
hWnd = _captureWin->m_hWnd;
|
||||
|
||||
POINT pt;
|
||||
pt.x = ev.mouse.x;
|
||||
pt.y = ev.mouse.y;
|
||||
mousePosToClient(_captureWin, pt);
|
||||
|
||||
ev.mouse.x = pt.x;
|
||||
ev.mouse.y = pt.y;
|
||||
return;
|
||||
}
|
||||
|
||||
// Special case for mouse moves: if there's an modal dialog,
|
||||
// mouse moves will still be routed to the main window
|
||||
// if the mouse is outside the dialog bounds
|
||||
CWnd *activeWin = _activeWindows.top();
|
||||
if (ev.type == Common::EVENT_MOUSEMOVE &&
|
||||
!activeWin->_windowRect.contains(ev.mouse)) {
|
||||
hWnd = _mainWindow->m_hWnd;
|
||||
return;
|
||||
}
|
||||
|
||||
CWnd *wnd = _activeWindows.top();
|
||||
hWnd = getMouseMessageWnd(ev, wnd);
|
||||
}
|
||||
|
||||
HWND EventLoop::getMouseMessageWnd(Common::Event &ev, CWnd *parent) {
|
||||
POINT pt;
|
||||
pt.x = ev.mouse.x;
|
||||
pt.y = ev.mouse.y;
|
||||
|
||||
if (!mousePosToClient(parent, pt))
|
||||
return nullptr;
|
||||
|
||||
// Iterate through any children
|
||||
for (const auto &node : parent->getChildren()) {
|
||||
HWND child = getMouseMessageWnd(ev, node._value);
|
||||
if (child)
|
||||
return child;
|
||||
}
|
||||
|
||||
// Final control under mouse
|
||||
ev.mouse.x = pt.x;
|
||||
ev.mouse.y = pt.y;
|
||||
return parent;
|
||||
}
|
||||
|
||||
bool EventLoop::mousePosToClient(CWnd *wnd, POINT &pt) {
|
||||
RECT clientRect;
|
||||
|
||||
// Get the mouse position in passed window
|
||||
wnd->ScreenToClient(&pt);
|
||||
wnd->GetClientRect(&clientRect);
|
||||
|
||||
Common::Rect r = clientRect;
|
||||
return r.contains(pt.x, pt.y);
|
||||
}
|
||||
|
||||
bool EventLoop::pollEvents(Common::Event &event) {
|
||||
return g_system->getEventManager()->pollEvent(event);
|
||||
}
|
||||
|
||||
void EventLoop::checkForFrameUpdate() {
|
||||
// Brief pauses and screen updates
|
||||
g_system->delayMillis(10);
|
||||
|
||||
// Trigger any pending timers
|
||||
triggerTimers();
|
||||
|
||||
// Handle screen updates
|
||||
uint32 time = g_system->getMillis();
|
||||
if (time >= _nextFrameTime) {
|
||||
_nextFrameTime = time + (1000 / FRAME_RATE);
|
||||
AfxGetApp()->getScreen()->update();
|
||||
}
|
||||
|
||||
// Cleanup any temporary handle wrapper
|
||||
AfxGetApp()->AfxUnlockTempMaps();
|
||||
}
|
||||
|
||||
bool EventLoop::PeekMessage(LPMSG lpMsg, HWND hWnd,
|
||||
unsigned int wMsgFilterMin, unsigned int wMsgFilterMax,
|
||||
unsigned int wRemoveMsg) {
|
||||
checkMessages();
|
||||
return _messages.peekMessage(lpMsg, hWnd,
|
||||
wMsgFilterMin, wMsgFilterMax, wRemoveMsg);
|
||||
}
|
||||
|
||||
bool EventLoop::PostMessage(HWND hWnd, unsigned int Msg,
|
||||
WPARAM wParam, LPARAM lParam) {
|
||||
if (shouldQuit())
|
||||
return false;
|
||||
if (!hWnd && Msg == WM_PARENTNOTIFY)
|
||||
// Hodj minigame launched directly without metagame,
|
||||
// so we can ignore the WM_PARENTNOTIFY on closure
|
||||
return false;
|
||||
|
||||
assert(hWnd);
|
||||
_messages.push(MSG(hWnd, Msg, wParam, lParam));
|
||||
return true;
|
||||
}
|
||||
|
||||
void EventLoop::TranslateMessage(LPMSG lpMsg) {
|
||||
if ((lpMsg->message == WM_KEYDOWN || lpMsg->message == WM_SYSKEYDOWN) &&
|
||||
(isChar((Common::KeyCode)lpMsg->wParam) || Common::isPrint(lpMsg->_ascii))) {
|
||||
uint message = (lpMsg->message == WM_SYSKEYDOWN) ?
|
||||
WM_SYSCHAR : WM_CHAR;
|
||||
WPARAM wParam = lpMsg->_ascii;
|
||||
LPARAM lParam = lpMsg->lParam;
|
||||
PostMessage(lpMsg->hwnd, message, wParam, lParam);
|
||||
}
|
||||
}
|
||||
|
||||
void EventLoop::DispatchMessage(LPMSG lpMsg) {
|
||||
CWnd *wnd = CWnd::FromHandle(lpMsg->hwnd);
|
||||
|
||||
if (wnd) {
|
||||
wnd->SendMessage(lpMsg->message,
|
||||
lpMsg->wParam, lpMsg->lParam);
|
||||
}
|
||||
}
|
||||
|
||||
bool EventLoop::isMouseMsg(const Common::Event &ev) const {
|
||||
return ev.type == Common::EVENT_MOUSEMOVE ||
|
||||
ev.type == Common::EVENT_LBUTTONDOWN ||
|
||||
ev.type == Common::EVENT_LBUTTONUP ||
|
||||
ev.type == Common::EVENT_RBUTTONDOWN ||
|
||||
ev.type == Common::EVENT_RBUTTONUP ||
|
||||
ev.type == Common::EVENT_WHEELUP ||
|
||||
ev.type == Common::EVENT_WHEELDOWN ||
|
||||
ev.type == Common::EVENT_MBUTTONDOWN ||
|
||||
ev.type == Common::EVENT_MBUTTONUP;;
|
||||
}
|
||||
|
||||
bool EventLoop::isJoystickMsg(const Common::Event &ev) const {
|
||||
return ev.type == Common::EVENT_JOYAXIS_MOTION ||
|
||||
ev.type == Common::EVENT_JOYBUTTON_DOWN ||
|
||||
ev.type == Common::EVENT_JOYBUTTON_UP;
|
||||
}
|
||||
|
||||
bool EventLoop::shouldQuit() const {
|
||||
return g_engine->shouldQuit();
|
||||
}
|
||||
|
||||
void EventLoop::quit() {
|
||||
g_engine->quitGame();
|
||||
}
|
||||
|
||||
void EventLoop::SetCapture(HWND hWnd) {
|
||||
_captureWin = hWnd;
|
||||
}
|
||||
|
||||
void EventLoop::ReleaseCapture() {
|
||||
_captureWin = nullptr;
|
||||
}
|
||||
|
||||
HWND EventLoop::GetCapture() const {
|
||||
return _captureWin;
|
||||
}
|
||||
|
||||
void EventLoop::SetFocus(CWnd *wnd) {
|
||||
HWND oldFocus = _focusedWin;
|
||||
HWND newFocus = wnd ? wnd->m_hWnd : nullptr;
|
||||
|
||||
if (newFocus != _focusedWin) {
|
||||
CWnd *focusedWin = CWnd::FromHandle(_focusedWin);
|
||||
CWnd *newFocusedWin = CWnd::FromHandle(newFocus);
|
||||
|
||||
if (_focusChangeProc)
|
||||
_focusChangeProc(focusedWin, newFocusedWin);
|
||||
|
||||
if (focusedWin) {
|
||||
focusedWin->_hasFocus = false;
|
||||
focusedWin->SendMessage(WM_KILLFOCUS,
|
||||
wnd ? (WPARAM)newFocus : (WPARAM)nullptr);
|
||||
}
|
||||
|
||||
_focusedWin = newFocus;
|
||||
if (wnd) {
|
||||
wnd->_hasFocus = true;
|
||||
wnd->SendMessage(WM_SETFOCUS, (WPARAM)oldFocus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EventLoop::setMousePos(const Common::Point &pt) {
|
||||
_mousePos = pt;
|
||||
g_system->warpMouse(pt.x, pt.y);
|
||||
}
|
||||
|
||||
MMRESULT EventLoop::joySetCapture(HWND hwnd, unsigned int uJoyID,
|
||||
unsigned int uPeriod, bool fChanged) {
|
||||
assert(uJoyID == JOYSTICKID1);
|
||||
_joystickWin = hwnd;
|
||||
return JOYERR_NOERROR;
|
||||
}
|
||||
|
||||
MMRESULT EventLoop::joySetThreshold(unsigned int uJoyID, unsigned int uThreshold) {
|
||||
// No implementation
|
||||
return JOYERR_NOERROR;
|
||||
}
|
||||
|
||||
MMRESULT EventLoop::joyGetPos(unsigned int uJoyID, LPJOYINFO pji) {
|
||||
assert(uJoyID == JOYSTICKID1);
|
||||
|
||||
pji->wXpos = _joystickPos.x;
|
||||
pji->wYpos = _joystickPos.y;
|
||||
pji->wZpos = 0;
|
||||
pji->wButtons = _joystickButtons;
|
||||
|
||||
return JOYERR_NOERROR;
|
||||
}
|
||||
|
||||
MMRESULT EventLoop::joyReleaseCapture(unsigned int uJoyID) {
|
||||
assert(uJoyID == JOYSTICKID1);
|
||||
return JOYERR_NOERROR;
|
||||
}
|
||||
|
||||
uintptr EventLoop::SetTimer(HWND hWnd, uintptr nIDEvent, unsigned int nElapse,
|
||||
void (CALLBACK *lpfnTimer)(HWND, unsigned int, uintptr, uint32)) {
|
||||
if (!nIDEvent)
|
||||
nIDEvent = ++_timerIdCtr;
|
||||
|
||||
_timers.push_back(TimerEntry(hWnd, nIDEvent, nElapse, lpfnTimer));
|
||||
|
||||
return nIDEvent;
|
||||
}
|
||||
|
||||
bool EventLoop::KillTimer(HWND hWnd, uintptr nIDEvent) {
|
||||
for (auto it = _timers.begin(); it != _timers.end(); ++it) {
|
||||
if (it->_hWnd == hWnd && it->_idEvent == nIDEvent) {
|
||||
_timers.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void EventLoop::triggerTimers() {
|
||||
uint32 currTime = g_system->getMillis();
|
||||
|
||||
for (auto it = _timers.begin(); it != _timers.end(); ) {
|
||||
if (currTime >= it->_nextTriggerTime) {
|
||||
// First update the timer for the next time
|
||||
it->_nextTriggerTime = currTime + it->_interval;
|
||||
|
||||
if (it->_callback) {
|
||||
// Call the callback
|
||||
it->_callback(it->_hWnd, WM_TIMER, it->_idEvent, currTime);
|
||||
} else {
|
||||
// Otherwise, send timer event
|
||||
CWnd *wnd = CWnd::FromHandle(it->_hWnd);
|
||||
if (wnd)
|
||||
wnd->SendMessage(WM_TIMER, it->_idEvent, 0);
|
||||
}
|
||||
|
||||
// Since it's conceivable that a timer callback might
|
||||
// remove timers, always restart the iterator afterwards
|
||||
it = _timers.begin();
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EventLoop::TimerEntry::TimerEntry(HWND hWnd, uintptr idEvent,
|
||||
uint32 interval, TimerProc callback) :
|
||||
_hWnd(hWnd), _idEvent(idEvent),
|
||||
_interval(interval), _callback(callback) {
|
||||
_nextTriggerTime = g_system->getMillis() + interval;
|
||||
}
|
||||
|
||||
void EventLoop::pause() {
|
||||
// Pause and update screen
|
||||
g_system->delayMillis(20);
|
||||
AfxGetApp()->getScreen()->update();
|
||||
AfxGetApp()->checkMessages();
|
||||
}
|
||||
|
||||
bool EventLoop::isChar(Common::KeyCode kc) const {
|
||||
return kc == Common::KEYCODE_SPACE ||
|
||||
kc == Common::KEYCODE_TAB ||
|
||||
kc == Common::KEYCODE_RETURN ||
|
||||
kc == Common::KEYCODE_BACKSPACE ||
|
||||
kc == Common::KEYCODE_ESCAPE;
|
||||
}
|
||||
|
||||
} // namespace Libs
|
||||
} // namespace MFC
|
||||
} // namespace Bagel
|
||||
270
engines/bagel/mfc/libs/event_loop.h
Normal file
270
engines/bagel/mfc/libs/event_loop.h
Normal file
@@ -0,0 +1,270 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BAGEL_MFC_LIBS_EVENT_LOOP_H
|
||||
#define BAGEL_MFC_LIBS_EVENT_LOOP_H
|
||||
|
||||
#include "common/events.h"
|
||||
#include "common/list.h"
|
||||
#include "common/stack.h"
|
||||
#include "bagel/mfc/minwindef.h"
|
||||
#include "bagel/mfc/libs/events.h"
|
||||
#include "bagel/mfc/joystickapi.h"
|
||||
|
||||
namespace Bagel {
|
||||
namespace MFC {
|
||||
namespace Libs {
|
||||
|
||||
typedef void(*TimerProc)(
|
||||
HWND hwnd, // handle of window associated with the timer (can be NULL)
|
||||
unsigned int uMsg, // WM_TIMER message (always WM_TIMER)
|
||||
uintptr idEvent,// timer identifier (from SetTimer or returned value)
|
||||
uint32 dwTime // system time when the callback was called (in milliseconds)
|
||||
);
|
||||
|
||||
typedef Common::KeyCode(*KeybindProc)(int key);
|
||||
typedef void (*FocusChangeProc)(CWnd *oldFocus, CWnd *newFocus);
|
||||
|
||||
#define JOYSTICK_REST_POS 32767
|
||||
|
||||
class WndList : public Common::List<CWnd *> {
|
||||
public:
|
||||
bool contains(CWnd *wnd) const {
|
||||
for (auto it = begin(); it != end(); ++it) {
|
||||
if (*it == wnd)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void add(CWnd *wnd) {
|
||||
if (!contains(wnd))
|
||||
push_back(wnd);
|
||||
}
|
||||
void remove(CWnd *wnd) {
|
||||
Common::List<CWnd *>::remove(wnd);
|
||||
}
|
||||
void clear() {
|
||||
Common::List<CWnd *>::clear();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class EventLoop {
|
||||
struct TimerEntry {
|
||||
HWND _hWnd = nullptr;
|
||||
uintptr _idEvent = 0;
|
||||
uint32 _interval = 0;
|
||||
uint32 _nextTriggerTime = 0;
|
||||
TimerProc _callback = nullptr;
|
||||
|
||||
TimerEntry() {}
|
||||
TimerEntry(HWND hWnd, uintptr idEvent,
|
||||
uint32 interval, TimerProc callback);
|
||||
};
|
||||
typedef Common::List<TimerEntry> TimerList;
|
||||
|
||||
private:
|
||||
CWnd *_mainWindow = nullptr;
|
||||
Common::Stack<CWnd *> _activeWindows;
|
||||
WndList _updateWnds;
|
||||
HWND _highlightedWin = nullptr;
|
||||
HWND _captureWin = nullptr;
|
||||
HWND _focusedWin = nullptr;
|
||||
HWND _joystickWin = nullptr;
|
||||
Libs::EventQueue _messages;
|
||||
TimerList _timers;
|
||||
int _timerIdCtr = 0;
|
||||
uint32 _nextFrameTime = 0;
|
||||
Common::Point _joystickPos = { JOYSTICK_REST_POS, JOYSTICK_REST_POS };
|
||||
Common::Point _mousePos;
|
||||
uint _joystickButtons = 0;
|
||||
Array<HOOKPROC> _kbdHookProc;
|
||||
int _idleCtr = 0;
|
||||
KeybindProc _keybindProc = nullptr;
|
||||
FocusChangeProc _focusChangeProc = nullptr;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Get any pending event
|
||||
* @return Returns false if app should quit
|
||||
*/
|
||||
bool GetMessage(MSG &msg);
|
||||
|
||||
/**
|
||||
* Gets the destination window for a message
|
||||
*/
|
||||
void setMessageWnd(Common::Event &ev, HWND &hWnd);
|
||||
|
||||
/**
|
||||
* Determine the window to send a mouse message to,
|
||||
* and translates the event mouse position to be
|
||||
* relative to the control's client rect
|
||||
*/
|
||||
void setMouseMessageWnd(Common::Event &ev, HWND &hWnd);
|
||||
HWND getMouseMessageWnd(Common::Event &ev, CWnd *parent);
|
||||
|
||||
/**
|
||||
* Returns true if the event is mouse related
|
||||
*/
|
||||
bool isMouseMsg(const Common::Event &ev) const;
|
||||
|
||||
/**
|
||||
* Returns true if the event is joystick related
|
||||
*/
|
||||
bool isJoystickMsg(const Common::Event &ev) const;
|
||||
|
||||
/**
|
||||
* Converts a position to be relative to a given window
|
||||
*/
|
||||
bool mousePosToClient(CWnd *wnd, POINT &pt);
|
||||
|
||||
/**
|
||||
* Trigger any pending timers
|
||||
*/
|
||||
void triggerTimers();
|
||||
|
||||
/**
|
||||
* Returns true if a keycode is one that
|
||||
* generates a WM_CHAR or WM_SYSCHAR event
|
||||
*/
|
||||
bool isChar(Common::KeyCode kc) const;
|
||||
|
||||
/**
|
||||
* Called when there are no pending messages.
|
||||
* Handles screen frame updates and timers
|
||||
*/
|
||||
void checkForFrameUpdate();
|
||||
|
||||
public:
|
||||
EventLoop() {}
|
||||
virtual ~EventLoop() {}
|
||||
|
||||
void runEventLoop(bool isModalDialog);
|
||||
void SetActiveWindow(CWnd *wnd);
|
||||
void PopActiveWindow();
|
||||
CWnd *GetActiveWindow() const {
|
||||
return _activeWindows.empty() ? nullptr :
|
||||
_activeWindows.top();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a modal dialog
|
||||
*/
|
||||
void doModal(CWnd *wnd);
|
||||
|
||||
/**
|
||||
* Polls for any pending messages and adds
|
||||
* them to the messages queue
|
||||
*/
|
||||
void checkMessages();
|
||||
|
||||
/**
|
||||
* Polls the ScummVM backend for any pending events.
|
||||
* Should be mostly only called internally by the main
|
||||
* event loop, but there are rare cases, like in the
|
||||
* Hodj n Podj movie window, where it was convenient to
|
||||
* to call in a simplified event loop.
|
||||
*/
|
||||
bool pollEvents(Common::Event &event);
|
||||
|
||||
/**
|
||||
* Checks the pending event queue for a message
|
||||
* with the specified criteria.
|
||||
*/
|
||||
bool PeekMessage(LPMSG lpMsg, HWND hWnd,
|
||||
unsigned int wMsgFilterMin, unsigned int wMsgFilterMax,
|
||||
unsigned int wRemoveMsg);
|
||||
|
||||
bool PostMessage(HWND hWnd, unsigned int Msg,
|
||||
WPARAM wParam, LPARAM lParam);
|
||||
|
||||
void TranslateMessage(LPMSG lpMsg);
|
||||
|
||||
void DispatchMessage(LPMSG lpMsg);
|
||||
|
||||
/**
|
||||
* Returns whether the app should quit.
|
||||
*/
|
||||
bool shouldQuit() const;
|
||||
|
||||
/**
|
||||
* Quit the game
|
||||
*/
|
||||
void quit();
|
||||
|
||||
void SetCapture(HWND hWnd);
|
||||
void ReleaseCapture();
|
||||
HWND GetCapture() const;
|
||||
Common::Point getMousePos() const {
|
||||
return _mousePos;
|
||||
}
|
||||
void setMousePos(const Common::Point &pt);
|
||||
|
||||
void SetFocus(CWnd *wnd);
|
||||
CWnd *GetFocus() const {
|
||||
return _focusedWin;
|
||||
}
|
||||
|
||||
MMRESULT joySetCapture(HWND hwnd, unsigned int uJoyID,
|
||||
unsigned int uPeriod, bool fChanged);
|
||||
MMRESULT joySetThreshold(unsigned int uJoyID, unsigned int uThreshold);
|
||||
MMRESULT joyGetPos(unsigned int uJoyID, LPJOYINFO pji);
|
||||
MMRESULT joyReleaseCapture(unsigned int uJoyID);
|
||||
|
||||
HHOOK HookKeyboard(HOOKPROC proc) {
|
||||
_kbdHookProc.push_back(proc);
|
||||
return (HHOOK)proc;
|
||||
}
|
||||
void UnhookKeyboard(HHOOK hook) {
|
||||
assert(_kbdHookProc.contains((HOOKPROC)hook));
|
||||
_kbdHookProc.remove((HOOKPROC)hook);
|
||||
}
|
||||
|
||||
uintptr SetTimer(HWND hWnd, uintptr nIDEvent, unsigned int nElapse,
|
||||
void (CALLBACK *lpfnTimer)(HWND, unsigned int, uintptr, uint32));
|
||||
bool KillTimer(HWND hWnd, uintptr nIDEvent);
|
||||
|
||||
void pause();
|
||||
|
||||
WndList &afxUpdateWnds() {
|
||||
return _updateWnds;
|
||||
}
|
||||
|
||||
virtual bool OnIdle(long lCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void setKeybinder(KeybindProc proc) {
|
||||
_keybindProc = proc;
|
||||
}
|
||||
|
||||
void setFocusChangeProc(FocusChangeProc proc) {
|
||||
_focusChangeProc = proc;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Libs
|
||||
} // namespace MFC
|
||||
} // namespace Bagel
|
||||
|
||||
#endif
|
||||
190
engines/bagel/mfc/libs/events.cpp
Normal file
190
engines/bagel/mfc/libs/events.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/system.h"
|
||||
#include "bagel/mfc/afxwin.h"
|
||||
#include "bagel/mfc/libs/events.h"
|
||||
|
||||
namespace Bagel {
|
||||
namespace MFC {
|
||||
namespace Libs {
|
||||
|
||||
int Event::_flags;
|
||||
int Event::_mouseX;
|
||||
int Event::_mouseY;
|
||||
|
||||
void Event::init() {
|
||||
_flags = 0;
|
||||
_mouseX = _mouseY = 0;
|
||||
}
|
||||
|
||||
Event::operator MSG() const {
|
||||
MSG msg;
|
||||
|
||||
// Reset fields
|
||||
msg.hwnd = (HWND)0;
|
||||
msg.message = 0;
|
||||
msg.wParam = msg.lParam = 0;
|
||||
msg.time = g_system->getMillis();
|
||||
|
||||
// Handle quit messages
|
||||
if (type == Common::EVENT_QUIT ||
|
||||
type == Common::EVENT_RETURN_TO_LAUNCHER) {
|
||||
msg.message = WM_QUIT;
|
||||
return msg;
|
||||
}
|
||||
|
||||
// For mouse events, set the position
|
||||
if (type >= Common::EVENT_MOUSEMOVE &&
|
||||
type <= Common::EVENT_MBUTTONUP) {
|
||||
_mouseX = mouse.x;
|
||||
_mouseY = mouse.y;
|
||||
|
||||
msg.lParam = MAKELPARAM(_mouseX, _mouseY);
|
||||
|
||||
switch (type) {
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
_flags |= MK_LBUTTON;
|
||||
break;
|
||||
case Common::EVENT_LBUTTONUP:
|
||||
_flags &= ~MK_LBUTTON;
|
||||
break;
|
||||
case Common::EVENT_RBUTTONDOWN:
|
||||
_flags |= MK_RBUTTON;
|
||||
break;
|
||||
case Common::EVENT_RBUTTONUP:
|
||||
_flags &= ~MK_RBUTTON;
|
||||
break;
|
||||
case Common::EVENT_MBUTTONDOWN:
|
||||
_flags |= MK_MBUTTON;
|
||||
break;
|
||||
case Common::EVENT_MBUTTONUP:
|
||||
_flags &= ~MK_MBUTTON;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
msg.wParam = _flags;
|
||||
}
|
||||
|
||||
if (type == Common::EVENT_KEYDOWN ||
|
||||
type == Common::EVENT_KEYUP) {
|
||||
// Update flags
|
||||
if (kbd.flags & Common::KBD_CTRL)
|
||||
_flags |= MK_CONTROL;
|
||||
else
|
||||
_flags &= ~MK_CONTROL;
|
||||
|
||||
if (kbd.flags & Common::KBD_SHIFT)
|
||||
_flags |= MK_SHIFT;
|
||||
else
|
||||
_flags &= ~MK_SHIFT;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case Common::EVENT_KEYDOWN:
|
||||
case Common::EVENT_KEYUP:
|
||||
if (kbd.flags & Common::KBD_ALT)
|
||||
msg.message = (type == Common::EVENT_KEYDOWN) ?
|
||||
WM_SYSKEYDOWN : WM_SYSKEYUP;
|
||||
else
|
||||
msg.message = (type == Common::EVENT_KEYDOWN) ?
|
||||
WM_KEYDOWN : WM_KEYUP;
|
||||
|
||||
msg.wParam = kbd.keycode;
|
||||
msg.lParam = (kbdRepeat ? 1 : 0) |
|
||||
((uint)kbd.keycode << 16) |
|
||||
((kbd.keycode >= 256 ? 1 : 0) << 24) |
|
||||
(((kbd.flags & Common::KBD_ALT) ? 1 : 0) << 29) |
|
||||
((type == Common::EVENT_KEYUP ? 1 : 0) << 30) |
|
||||
((type == Common::EVENT_KEYDOWN ? 1 : 0) << 31);
|
||||
msg._kbdFlags = kbd.flags;
|
||||
msg._ascii = kbd.ascii;
|
||||
break;
|
||||
|
||||
case Common::EVENT_MOUSEMOVE:
|
||||
msg.message = WM_MOUSEMOVE;
|
||||
break;
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
msg.message = WM_LBUTTONDOWN;
|
||||
break;
|
||||
case Common::EVENT_LBUTTONUP:
|
||||
msg.message = WM_LBUTTONUP;
|
||||
break;
|
||||
case Common::EVENT_RBUTTONDOWN:
|
||||
msg.message = WM_RBUTTONDOWN;
|
||||
break;
|
||||
case Common::EVENT_RBUTTONUP:
|
||||
msg.message = WM_RBUTTONUP;
|
||||
break;
|
||||
case Common::EVENT_MBUTTONDOWN:
|
||||
msg.message = WM_MBUTTONDOWN;
|
||||
break;
|
||||
case Common::EVENT_MBUTTONUP:
|
||||
msg.message = WM_MBUTTONUP;
|
||||
break;
|
||||
default:
|
||||
msg.message = WM_NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/*--------------------------------------------*/
|
||||
|
||||
MSG EventQueue::pop() {
|
||||
assert(!empty());
|
||||
MSG result = _queue.back();
|
||||
_queue.remove_at(_queue.size() - 1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool EventQueue::peekMessage(MSG *lpMsg, HWND hWnd,
|
||||
unsigned int wMsgFilterMin, unsigned int wMsgFilterMax,
|
||||
unsigned int wRemoveMsg) {
|
||||
bool result = false;
|
||||
|
||||
for (uint i = 0; i < _queue.size(); ++i) {
|
||||
const MSG &msg = _queue[i];
|
||||
if ((hWnd == nullptr || hWnd == msg.hwnd) &&
|
||||
((wMsgFilterMin == 0 && wMsgFilterMax == 0) ||
|
||||
(msg.message == WM_QUIT) ||
|
||||
(msg.message >= wMsgFilterMin && msg.message <= wMsgFilterMax))) {
|
||||
// Found a matching message
|
||||
*lpMsg = msg;
|
||||
|
||||
if (wRemoveMsg & PM_REMOVE)
|
||||
// Remove the message
|
||||
_queue.remove_at(i);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Libs
|
||||
} // namespace MFC
|
||||
} // namespace Bagel
|
||||
82
engines/bagel/mfc/libs/events.h
Normal file
82
engines/bagel/mfc/libs/events.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BAGEL_MFC_LIBS_EVENTS_H
|
||||
#define BAGEL_MFC_LIBS_EVENTS_H
|
||||
|
||||
#include "common/events.h"
|
||||
#include "bagel/mfc/minwindef.h"
|
||||
|
||||
namespace Bagel {
|
||||
namespace MFC {
|
||||
namespace Libs {
|
||||
|
||||
class Event : public Common::Event {
|
||||
private:
|
||||
static int _mouseX, _mouseY;
|
||||
static int _flags;
|
||||
|
||||
public:
|
||||
static void init();
|
||||
|
||||
operator MSG() const;
|
||||
};
|
||||
|
||||
class EventQueue {
|
||||
private:
|
||||
Common::Array<MSG> _queue;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Returns true if the queue is empty
|
||||
* @return true if empty
|
||||
*/
|
||||
bool empty() const {
|
||||
return _queue.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a message to the queue
|
||||
* @param msg Message to add
|
||||
*/
|
||||
void push(const MSG &msg) {
|
||||
_queue.insert_at(0, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a message from the queue.
|
||||
* @return
|
||||
*/
|
||||
MSG pop();
|
||||
|
||||
/**
|
||||
* Checks the queue for a given message
|
||||
*/
|
||||
bool peekMessage(MSG *lpMsg, HWND hWnd,
|
||||
unsigned int wMsgFilterMin, unsigned int wMsgFilterMax,
|
||||
unsigned int wRemoveMsg);
|
||||
};
|
||||
|
||||
} // namespace Libs
|
||||
} // namespace MFC
|
||||
} // namespace Bagel
|
||||
|
||||
#endif
|
||||
47
engines/bagel/mfc/libs/list.h
Normal file
47
engines/bagel/mfc/libs/list.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BAGEL_MFC_LIBS_LIST_H
|
||||
#define BAGEL_MFC_LIBS_LIST_H
|
||||
|
||||
#include "common/list.h"
|
||||
|
||||
namespace Bagel {
|
||||
namespace MFC {
|
||||
namespace Libs {
|
||||
|
||||
template<class T>
|
||||
class List : public Common::List<T> {
|
||||
public:
|
||||
bool contains(const T &tmp) const {
|
||||
for (auto it = this->begin(); it != this->end(); ++it) {
|
||||
if (*it == tmp)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Libs
|
||||
} // namespace MFC
|
||||
} // namespace Bagel
|
||||
|
||||
#endif
|
||||
111
engines/bagel/mfc/libs/resources.cpp
Normal file
111
engines/bagel/mfc/libs/resources.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/formats/winexe_ne.h"
|
||||
#include "bagel/mfc/libs/resources.h"
|
||||
#include "bagel/mfc/global_functions.h"
|
||||
|
||||
namespace Bagel {
|
||||
namespace MFC {
|
||||
namespace Libs {
|
||||
|
||||
Resources::~Resources() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void Resources::addResources(const Common::Path &file) {
|
||||
Common::NEResources *res = new Common::NEResources();
|
||||
if (!res->loadFromEXE(file))
|
||||
error("Could not load %s", file.baseName().c_str());
|
||||
|
||||
push_front(ResourceFile());
|
||||
front()._filename = file.baseName();
|
||||
front()._res = res;
|
||||
_cache.clear();
|
||||
}
|
||||
|
||||
void Resources::removeResources(const Common::Path &file) {
|
||||
// Expect resources to be removed in order
|
||||
assert(file.baseName() == front()._filename);
|
||||
pop_front();
|
||||
_cache.clear();
|
||||
}
|
||||
|
||||
HRSRC Resources::findResource(const char *lpName, const char *lpType) {
|
||||
if (empty())
|
||||
error("Use CWinApp::addResources to register "
|
||||
"an .exe or .dll file containing the resources");
|
||||
|
||||
Common::WinResourceID type =
|
||||
(HIWORD(lpType) == 0) ?
|
||||
Common::WinResourceID((intptr)lpType) :
|
||||
Common::WinResourceID(lpType);
|
||||
|
||||
Common::WinResourceID id;
|
||||
if (HIWORD(lpName) == 0)
|
||||
id = Common::WinResourceID((intptr)lpName);
|
||||
else if (*lpName == '#')
|
||||
id = Common::WinResourceID(atol(lpName + 1));
|
||||
else
|
||||
id = Common::WinResourceID(lpName);
|
||||
|
||||
// First check the cache
|
||||
for (auto &it : _cache) {
|
||||
if (it._type == type && it._id == id)
|
||||
return (HRSRC)⁢
|
||||
}
|
||||
|
||||
// Get the resource
|
||||
for (auto &it : (*this)) {
|
||||
Common::WinResources *res = it._res;
|
||||
Common::SeekableReadStream *rs = res->getResource(type, id);
|
||||
|
||||
if (rs) {
|
||||
// Add it to the cache, since we'll use
|
||||
// a pointer to it's entry as the return
|
||||
_cache.push_back(Resource(res, type, id, rs->size()));
|
||||
|
||||
delete rs;
|
||||
return (HRSRC)&_cache.back();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HGLOBAL Resources::loadResource(HRSRC hResInfo) {
|
||||
Resource *ptr = ((Resource *)hResInfo);
|
||||
Common::SeekableReadStream *rs = ptr->_file->getResource(
|
||||
ptr->_type, ptr->_id);
|
||||
|
||||
HGLOBAL h = GlobalAlloc(GPTR, ptr->_size);
|
||||
byte *data = (byte *)GlobalLock(h);
|
||||
rs->read(data, ptr->_size);
|
||||
GlobalUnlock(rs);
|
||||
|
||||
delete rs;
|
||||
return h;
|
||||
}
|
||||
|
||||
} // namespace Libs
|
||||
} // namespace MFC
|
||||
} // namespace Bagel
|
||||
104
engines/bagel/mfc/libs/resources.h
Normal file
104
engines/bagel/mfc/libs/resources.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BAGEL_MFC_LIBS_RESOURCES_H
|
||||
#define BAGEL_MFC_LIBS_RESOURCES_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/path.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/formats/winexe.h"
|
||||
#include "bagel/mfc/minwindef.h"
|
||||
|
||||
namespace Bagel {
|
||||
namespace MFC {
|
||||
namespace Libs {
|
||||
|
||||
struct Resource {
|
||||
Common::WinResources *_file = nullptr;
|
||||
Common::WinResourceID _type;
|
||||
Common::WinResourceID _id;
|
||||
size_t _size = 0;
|
||||
|
||||
Resource() {}
|
||||
Resource(Common::WinResources *file,
|
||||
const Common::WinResourceID &type,
|
||||
Common::WinResourceID &id, size_t size) :
|
||||
_file(file), _type(type), _id(id),
|
||||
_size(size) {}
|
||||
};
|
||||
typedef Common::List<Resource> ResCache;
|
||||
|
||||
struct ResourceFile {
|
||||
Common::String _filename;
|
||||
Common::WinResources *_res = nullptr;
|
||||
|
||||
~ResourceFile() {
|
||||
delete _res;
|
||||
}
|
||||
};
|
||||
typedef Common::List<ResourceFile> ResList;
|
||||
|
||||
class Resources : public ResList {
|
||||
private:
|
||||
ResCache _cache;
|
||||
|
||||
public:
|
||||
~Resources();
|
||||
|
||||
/**
|
||||
* Adds a Windows file containing resources
|
||||
*/
|
||||
void addResources(const Common::Path &file);
|
||||
|
||||
/**
|
||||
* Removes a Windows file containing resources
|
||||
*/
|
||||
void removeResources(const Common::Path &file);
|
||||
|
||||
#if 0
|
||||
Common::WinResources *getCoreResources() const {
|
||||
return _resources.begin()->_value;
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* Find a resource
|
||||
*/
|
||||
HRSRC findResource(const char *lpName, const char *lpType);
|
||||
|
||||
/**
|
||||
* Return the size of a resource
|
||||
*/
|
||||
size_t resourceSize(HRSRC res) const {
|
||||
return ((Resource *)res)->_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a resource as a memory block
|
||||
*/
|
||||
HGLOBAL loadResource(HRSRC hResInfo);
|
||||
};
|
||||
|
||||
} // namespace Libs
|
||||
} // namespace MFC
|
||||
} // namespace Bagel
|
||||
|
||||
#endif
|
||||
146
engines/bagel/mfc/libs/settings.cpp
Normal file
146
engines/bagel/mfc/libs/settings.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/system.h"
|
||||
#include "engines/engine.h"
|
||||
#include "bagel/mfc/libs/settings.h"
|
||||
|
||||
namespace Bagel {
|
||||
namespace MFC {
|
||||
namespace Libs {
|
||||
|
||||
Common::String Settings::getFilename() {
|
||||
// Hack to get engine target name
|
||||
Common::String fname = g_engine->getSaveStateName(0);
|
||||
fname = Common::String(fname.c_str(), strchr(fname.c_str(), '.'));
|
||||
fname += "-settings.ini";
|
||||
|
||||
return fname;
|
||||
}
|
||||
|
||||
void Settings::load() {
|
||||
Common::InSaveFile *src = g_system->getSavefileManager()->openForLoading(getFilename());
|
||||
Common::String domainName;
|
||||
|
||||
if (src) {
|
||||
while (!src->eos()) {
|
||||
domainName = Domain::getDomainName(src);
|
||||
if (domainName.empty())
|
||||
break;
|
||||
|
||||
_domains[domainName].load(src);
|
||||
}
|
||||
}
|
||||
|
||||
delete src;
|
||||
}
|
||||
|
||||
void Settings::save() {
|
||||
if (_domains.empty() || !isModified())
|
||||
return;
|
||||
|
||||
Common::OutSaveFile *dest = g_system->getSavefileManager()->openForSaving(getFilename(), false);
|
||||
if (!dest)
|
||||
return;
|
||||
Common::String domainName;
|
||||
|
||||
for (Domains::iterator it = _domains.begin(); it != _domains.end(); ++it) {
|
||||
if (it->_value.empty())
|
||||
continue;
|
||||
|
||||
domainName = it->_key;
|
||||
dest->writeByte('[');
|
||||
dest->writeString(it->_key);
|
||||
dest->writeByte(']');
|
||||
dest->writeByte('\n');
|
||||
|
||||
it->_value.save(dest);
|
||||
}
|
||||
|
||||
dest->finalize();
|
||||
delete dest;
|
||||
}
|
||||
|
||||
bool Settings::isModified() const {
|
||||
for (Domains::iterator it = _domains.begin();
|
||||
it != _domains.end(); ++it) {
|
||||
if (it->_value.isModified())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Settings::Domain &Settings::operator[](const Common::String &domain) {
|
||||
Settings::Domain &result = _domains[domain];
|
||||
result._settings = this;
|
||||
return result;
|
||||
}
|
||||
|
||||
Common::String Settings::Domain::getDomainName(Common::InSaveFile *src) {
|
||||
Common::String line = src->readLine();
|
||||
if (line.empty())
|
||||
return "";
|
||||
|
||||
assert(line.hasPrefix("[") && line.hasSuffix("]"));
|
||||
return Common::String(line.c_str() + 1, line.size() - 2);
|
||||
}
|
||||
|
||||
void Settings::Domain::load(Common::InSaveFile *src) {
|
||||
Common::String str;
|
||||
uint equals;
|
||||
|
||||
for (;;) {
|
||||
str = src->readLine();
|
||||
if (str.empty())
|
||||
break;
|
||||
|
||||
equals = str.findFirstOf('=');
|
||||
assert(equals != Common::String::npos);
|
||||
|
||||
_values[Common::String(str.c_str(),
|
||||
str.c_str() + equals)] =
|
||||
Common::String(str.c_str() + equals + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void Settings::Domain::save(Common::OutSaveFile *dest) {
|
||||
Common::String str;
|
||||
|
||||
for (Values::iterator it = _values.begin(); it != _values.end(); ++it) {
|
||||
str = Common::String::format("%s=%s",
|
||||
it->_key.c_str(), it->_value.c_str());
|
||||
dest->writeString(str);
|
||||
dest->writeByte('\n');
|
||||
}
|
||||
|
||||
dest->writeByte('\n');
|
||||
_modified = false;
|
||||
}
|
||||
|
||||
void Settings::Domain::flushToDisk() {
|
||||
assert(_settings);
|
||||
_settings->save();
|
||||
}
|
||||
|
||||
} // namespace Libs
|
||||
} // namespace MFC
|
||||
} // namespace Bagel
|
||||
141
engines/bagel/mfc/libs/settings.h
Normal file
141
engines/bagel/mfc/libs/settings.h
Normal file
@@ -0,0 +1,141 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BAGEL_MFC_LIBS_SETTINGS_H
|
||||
#define BAGEL_MFC_LIBS_SETTINGS_H
|
||||
|
||||
#include "common/hashmap.h"
|
||||
#include "common/hash-str.h"
|
||||
#include "common/savefile.h"
|
||||
|
||||
namespace Bagel {
|
||||
namespace MFC {
|
||||
namespace Libs {
|
||||
|
||||
class Settings {
|
||||
public:
|
||||
class Domain {
|
||||
friend class Settings;
|
||||
private:
|
||||
typedef Common::HashMap<Common::String, Common::String,
|
||||
Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> Values;
|
||||
Settings *_settings = nullptr;
|
||||
Values _values;
|
||||
bool _modified = false;
|
||||
|
||||
public:
|
||||
void load(Common::InSaveFile *src);
|
||||
void save(Common::OutSaveFile *dest);
|
||||
static Common::String getDomainName(Common::InSaveFile *src);
|
||||
|
||||
bool isModified() const {
|
||||
return _modified;
|
||||
}
|
||||
bool empty() const {
|
||||
return _values.empty();
|
||||
}
|
||||
|
||||
bool hasKey(const Common::String &key) const {
|
||||
return _values.contains(key);
|
||||
}
|
||||
int getInt(const Common::String &key, int defaultValue = 0) const {
|
||||
return hasKey(key) ? atoi(_values[key].c_str()) : defaultValue;
|
||||
}
|
||||
bool getBool(const Common::String &key, bool defaultValue = false) const {
|
||||
return !hasKey(key) || _values[key].empty() ? false :
|
||||
tolower(_values[key][0]) == 't';
|
||||
}
|
||||
Common::String getString(const Common::String &key, const char *defaultValue = nullptr) const {
|
||||
return hasKey(key) ? _values[key] : defaultValue;
|
||||
}
|
||||
void setInt(const Common::String &key, int value) {
|
||||
_values[key] = Common::String::format("%d", value);
|
||||
_modified = true;
|
||||
}
|
||||
void setBool(const Common::String &key, bool value) {
|
||||
_values[key] = value ? "true" : "false";
|
||||
_modified = true;
|
||||
}
|
||||
void setString(const Common::String &key, const Common::String &value) {
|
||||
_values[key] = value;
|
||||
_modified = true;
|
||||
}
|
||||
|
||||
void flushToDisk();
|
||||
};
|
||||
|
||||
class Serializer {
|
||||
private:
|
||||
Domain &_domain;
|
||||
bool _isSaving;
|
||||
public:
|
||||
Serializer(Domain &domain, bool isSaving) :
|
||||
_domain(domain), _isSaving(isSaving) {
|
||||
}
|
||||
~Serializer() {
|
||||
_domain.flushToDisk();
|
||||
}
|
||||
|
||||
void sync(const Common::String &key, int &field, int defaultValue = 0) {
|
||||
if (_isSaving)
|
||||
_domain.setInt(key, field);
|
||||
else
|
||||
field = _domain.getInt(key, defaultValue);
|
||||
}
|
||||
void sync(const Common::String &key, bool &field, bool defaultValue = false) {
|
||||
if (_isSaving)
|
||||
_domain.setBool(key, field);
|
||||
else
|
||||
field = _domain.getBool(key, defaultValue);
|
||||
}
|
||||
void sync(const Common::String &key, Common::String &field, const char *defaultValue = nullptr) {
|
||||
if (_isSaving)
|
||||
_domain.setString(key, field);
|
||||
else
|
||||
field = _domain.getString(key, defaultValue);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
static Common::String getFilename();
|
||||
|
||||
public:
|
||||
~Settings() {
|
||||
save();
|
||||
}
|
||||
|
||||
void load();
|
||||
void save();
|
||||
|
||||
bool isModified() const;
|
||||
Domain &operator[](const Common::String &domain);
|
||||
|
||||
private:
|
||||
typedef Common::HashMap<Common::String, Domain,
|
||||
Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> Domains;
|
||||
Domains _domains;
|
||||
};
|
||||
|
||||
} // namespace Libs
|
||||
} // namespace MFC
|
||||
} // namespace Bagel
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user