Supermodel/Src/OSD/Windows/WinOutputs.cpp

265 lines
7.1 KiB
C++
Raw Normal View History

Committing various small updates that have been hanging around in my source tree for a while now: - Added 'crosshairs' command line and config option. - Added 'vsync' command line and config option (so far only tested on NVidia cards on Windows 7 - other graphics drivers, O/Ss or driver settings may simply chose to ignore this). - Added fullscreen toggle within game using Alt+Enter key combination. - Added framework for lamp outputs and 'outputs' command line and config option. So far only the lamps for driving games are hooked up in the emulator (others to be added later). - Added an initial outputs implementation for Windows that sends MAMEHooker compatible messages (-outputs=win to enable) - Fixed fps calculation in Main.cpp that was producing incorrect results and so giving the impression that frame throttling wasn't working properly when in fact it was. - Fixed palette indexed colours as the index was always off by one, causing incorrect colours in various games, eg drivers' suits and flashing Start sign in Daytona 2. - Altered caching of models so that models with palette indexed colours use the dynamic cache rather than the static one. This is so that changes in palette indexed colours appear on screen, eg the flashing Start sign on the advanced course of Daytona 2 (although currently the START message itself is not visible due to other problems with texture decoding). - Fixed small bug in TileGen.cpp which meant both palettes were being completely recomputed pretty much with every frame. This was a significant performance hit, particularly as palette recomputation is currently being done in SyncSnapshots (it should be moved out of here at some point, although for now it's no big deal). - Made sure all OpenGL objects and resources are deleted in Render2D/3D destructors, in particular the deleting of the VBO buffer in DestroyModelCache. - Made sure that GLSL uniforms are always checked to see if they are bound before using them in order to stop unecessary (but harmless) GL errors. - Altered the default texture sheet handling to use a single large GL texture holding multiple Model3 texture sheets rather than multiple GL textures as before (if required, the old behaviour can still be selected with the mulisheet fragment shader). I believe this fixes the disappearing crosshairs/corrupt GL state problem which the multisheet fragment shader seemed to be triggering somehow. - Fixed a bug in debugger which meant memory watches were not triggering properly
2012-07-15 21:04:46 +00:00
/**
** 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);
}
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::AllocateMessageId(UINT &regId, 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
const char *name;
if (id == 0)
name = GetGame()->id;
else
name = MapIdToName(id);
// NULL name is an empty string
if (name == NULL)
name = "";
// Allocate memory for message
int dataLen = sizeof(CopyDataIdString) + strlen(name);
CopyDataIdString *data = (CopyDataIdString*)new(nothrow) UINT8[dataLen];
if (!data)
return 1;
data->id = id;
strcpy(data->string, name);
// 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)&copyData);
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;
}