/** ** 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 . **/ /* * WinOutputs.cpp */ #include #include #include #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::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::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::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; }