mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-25 07:05:40 +00:00
274 lines
7.3 KiB
C++
Executable file
274 lines
7.3 KiB
C++
Executable file
/**
|
|
** Supermodel
|
|
** A Sega Model 3 Arcade Emulator.
|
|
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
|
**
|
|
** This file is part of Supermodel.
|
|
**
|
|
** Supermodel 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.
|
|
**
|
|
** Supermodel 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 Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
|
**/
|
|
|
|
/*
|
|
* WinOutputs.cpp
|
|
*/
|
|
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
|
|
#include "OSD/Windows/WinOutputs.h"
|
|
#include "Supermodel.h"
|
|
|
|
#define OUTPUT_WINDOW_CLASS TEXT("MAMEOutput")
|
|
#define OUTPUT_WINDOW_NAME TEXT("MAMEOutput")
|
|
|
|
// Messages sent from emulator to external clients
|
|
#define START TEXT("MAMEOutputStart")
|
|
#define STOP TEXT("MAMEOutputStop")
|
|
#define UPDATE_STATE TEXT("MAMEOutputUpdateState")
|
|
|
|
// Messages sent from external clients to emulator
|
|
#define REGISTER_CLIENT TEXT("MAMEOutputRegister")
|
|
#define UNREGISTER_CLIENT TEXT("MAMEOutputUnregister")
|
|
#define GET_ID_STRING TEXT("MAMEOutputGetIDString")
|
|
|
|
#define COPYDATA_MESSAGE_ID_STRING 1
|
|
|
|
bool CWinOutputs::s_createdClass = false;
|
|
|
|
CWinOutputs::CWinOutputs() : m_hwnd(NULL)
|
|
{
|
|
//
|
|
}
|
|
|
|
CWinOutputs::~CWinOutputs()
|
|
{
|
|
// Broadcast a shutdown message
|
|
if (m_hwnd)
|
|
PostMessage(HWND_BROADCAST, m_onStop, (WPARAM)m_hwnd, 0);
|
|
DeleteWindowClass();
|
|
}
|
|
|
|
bool CWinOutputs::Initialize()
|
|
{
|
|
// Create window class
|
|
if (!CreateWindowClass())
|
|
{
|
|
ErrorLog("Unable to register window class for Windows outputs");
|
|
return false;
|
|
}
|
|
|
|
// Create window
|
|
m_hwnd = CreateWindowEx(0,
|
|
OUTPUT_WINDOW_CLASS,
|
|
OUTPUT_WINDOW_NAME,
|
|
WS_OVERLAPPEDWINDOW,
|
|
0, 0,
|
|
1, 1,
|
|
NULL,
|
|
NULL,
|
|
GetModuleHandle(NULL),
|
|
NULL);
|
|
if (!m_hwnd)
|
|
{
|
|
ErrorLog("Unable to create window handle for Windows outputs");
|
|
return false;
|
|
}
|
|
|
|
// Allocate message ids
|
|
if (!AllocateMessageId(m_onStart, START)) return false;
|
|
if (!AllocateMessageId(m_onStop, STOP)) return false;
|
|
if (!AllocateMessageId(m_updateState, UPDATE_STATE)) return false;
|
|
if (!AllocateMessageId(m_regClient, REGISTER_CLIENT)) return false;
|
|
if (!AllocateMessageId(m_unregClient, UNREGISTER_CLIENT)) return false;
|
|
if (!AllocateMessageId(m_getIdString, GET_ID_STRING)) return false;
|
|
|
|
// Set pointer to this object
|
|
SetWindowLongPtr(m_hwnd, GWLP_USERDATA, (LONG_PTR)this);
|
|
return true;
|
|
}
|
|
|
|
void CWinOutputs::Attached()
|
|
{
|
|
// Broadcast a startup message
|
|
PostMessage(HWND_BROADCAST, m_onStart, (WPARAM)m_hwnd, 0);
|
|
}
|
|
|
|
void CWinOutputs::SendOutput(EOutputs output, UINT8 prevValue, UINT8 value)
|
|
{
|
|
//printf("LAMP OUTPUT %s = %u -> %u\n", GetOutputName(output), prevValue, value);
|
|
|
|
// Loop through all registered clients and send them new output value
|
|
LPARAM param = (LPARAM)output + 1;
|
|
for (vector<RegisteredClient>::iterator it = m_clients.begin(), end = m_clients.end(); it != end; ++it)
|
|
PostMessage(it->hwnd, m_updateState, param, value);
|
|
}
|
|
|
|
LRESULT CALLBACK CWinOutputs::OutputWindowProcCallback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LONG_PTR ptr = GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
CWinOutputs *outputs = (CWinOutputs*)ptr;
|
|
if (!outputs)
|
|
return 1;
|
|
return outputs->OutputWindowProc(hwnd, msg, wParam, lParam);
|
|
}
|
|
|
|
bool CWinOutputs::CreateWindowClass()
|
|
{
|
|
if (s_createdClass)
|
|
return true;
|
|
|
|
// Setup description of window class
|
|
WNDCLASS wc = { 0 };
|
|
wc.lpszClassName = OUTPUT_WINDOW_CLASS;
|
|
wc.hInstance = GetModuleHandle(NULL);
|
|
wc.lpfnWndProc = OutputWindowProcCallback;
|
|
|
|
// Register class
|
|
if (RegisterClass(&wc))
|
|
{
|
|
s_createdClass = true;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
bool CWinOutputs::DeleteWindowClass()
|
|
{
|
|
if (!s_createdClass)
|
|
return true;
|
|
|
|
// Register class
|
|
if (UnregisterClass(OUTPUT_WINDOW_CLASS, GetModuleHandle(NULL))) {
|
|
s_createdClass = false;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
bool CWinOutputs::AllocateMessageId(UINT ®Id, LPCSTR str)
|
|
{
|
|
regId = RegisterWindowMessage(str);
|
|
if (regId != 0)
|
|
return true;
|
|
ErrorLog("Unable to register window message '%s' for Windows outputs", str);
|
|
return false;
|
|
}
|
|
|
|
LRESULT CWinOutputs::OutputWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
// Process message sent to emulator window
|
|
if (msg == m_regClient) return RegisterClient((HWND)wParam, lParam);
|
|
else if (msg == m_unregClient) return UnregisterClient((HWND)wParam, lParam);
|
|
else if (msg == m_getIdString) return SendIdString((HWND)wParam, lParam);
|
|
else return DefWindowProc(hwnd, msg, wParam, lParam);
|
|
}
|
|
|
|
LRESULT CWinOutputs::RegisterClient(HWND hwnd, LPARAM id)
|
|
{
|
|
// Check that given client is not already registered
|
|
for (vector<RegisteredClient>::iterator it = m_clients.begin(), end = m_clients.end(); it != end; ++it)
|
|
{
|
|
if (it->id == id)
|
|
{
|
|
it->hwnd = hwnd;
|
|
|
|
// If so, just send it current state of all outputs
|
|
SendAllToClient(*it);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// If not, store details about client and send it current state of all outputs
|
|
RegisteredClient client;
|
|
client.id = id;
|
|
client.hwnd = hwnd;
|
|
m_clients.push_back(client);
|
|
|
|
SendAllToClient(client);
|
|
return 0;
|
|
}
|
|
|
|
void CWinOutputs::SendAllToClient(RegisteredClient &client)
|
|
{
|
|
// Loop through all known outputs and send their current state to given client
|
|
for (unsigned i = 0; i < NUM_OUTPUTS; i++)
|
|
{
|
|
EOutputs output = (EOutputs)i;
|
|
LPARAM param = (LPARAM)output + 1;
|
|
PostMessage(client.hwnd, m_updateState, param, GetValue(output));
|
|
}
|
|
}
|
|
|
|
LRESULT CWinOutputs::UnregisterClient(HWND hwnd, LPARAM id)
|
|
{
|
|
// Find any matching clients and remove them
|
|
bool found = false;
|
|
vector<RegisteredClient>::iterator it = m_clients.begin();
|
|
while (it != m_clients.end())
|
|
{
|
|
if (it->id == id)
|
|
{
|
|
it = m_clients.erase(it);
|
|
found = true;
|
|
}
|
|
else
|
|
++it;
|
|
}
|
|
|
|
// Return error if no matches found
|
|
return (found ? 0 : 1);
|
|
}
|
|
|
|
LRESULT CWinOutputs::SendIdString(HWND hwnd, LPARAM id)
|
|
{
|
|
// Id 0 is the name of the game
|
|
std::string name;
|
|
if (id == 0)
|
|
name = GetGame().name;
|
|
else
|
|
name = MapIdToName(id) ? MapIdToName(id) : "";
|
|
|
|
// Allocate memory for message
|
|
int dataLen = sizeof(CopyDataIdString) + name.length();
|
|
CopyDataIdString *data = (CopyDataIdString*)new(nothrow) UINT8[dataLen];
|
|
if (!data)
|
|
return 1;
|
|
data->id = id;
|
|
strcpy(data->string, name.c_str());
|
|
|
|
// Reply by using SendMessage with WM_COPYDATA
|
|
COPYDATASTRUCT copyData;
|
|
copyData.dwData = COPYDATA_MESSAGE_ID_STRING;
|
|
copyData.cbData = dataLen;
|
|
copyData.lpData = data;
|
|
SendMessage(hwnd, WM_COPYDATA, (WPARAM)m_hwnd, (LPARAM)©Data);
|
|
|
|
delete[] data;
|
|
return 0;
|
|
}
|
|
|
|
const char *CWinOutputs::MapIdToName(LPARAM id)
|
|
{
|
|
EOutputs output = (EOutputs)(id - 1);
|
|
return GetOutputName(output);
|
|
}
|
|
|
|
LPARAM CWinOutputs::MapNameToId(const char *name)
|
|
{
|
|
EOutputs output = GetOutputByName(name);
|
|
if (output == OutputUnknown)
|
|
return 0;
|
|
return (LPARAM)output + 1;
|
|
} |