Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View 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

View 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

View 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

View 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

View 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

View 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

View 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)&it;
}
// 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

View 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

View 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

View 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