diff options
author | 2016-06-05 21:11:56 +0200 | |
---|---|---|
committer | 2016-06-05 21:11:56 +0200 | |
commit | dba7c1ac9420110762b635cffe605190399251e5 (patch) | |
tree | 0e0598f22988eacd7daf15874972b1f53d5e8dcc | |
parent | eb2617f7ae2d4d052de0cdbb4c7dfff3d8dc2c96 (diff) |
Placed back old output system as module "-output windows" need more things cleaned (nw)
-rw-r--r-- | scripts/src/osd/modules.lua | 2 | ||||
-rw-r--r-- | scripts/src/osd/windows.lua | 32 | ||||
-rw-r--r-- | src/emu/output.cpp | 5 | ||||
-rw-r--r-- | src/emu/output.h | 3 | ||||
-rw-r--r-- | src/osd/modules/lib/osdobj_common.cpp | 2 | ||||
-rw-r--r-- | src/osd/modules/output/output_module.h | 7 | ||||
-rw-r--r-- | src/osd/modules/output/win32_output.cpp | 336 | ||||
-rw-r--r-- | src/osd/modules/output/win32_output.h | 86 | ||||
-rw-r--r-- | src/osd/windows/ledutil.cpp | 709 |
9 files changed, 1178 insertions, 4 deletions
diff --git a/scripts/src/osd/modules.lua b/scripts/src/osd/modules.lua index a05e55ee4c6..5fb1d23c1bc 100644 --- a/scripts/src/osd/modules.lua +++ b/scripts/src/osd/modules.lua @@ -96,6 +96,8 @@ function osdmodulesbuild() MAME_DIR .. "src/osd/modules/output/none.cpp", MAME_DIR .. "src/osd/modules/output/console.cpp", MAME_DIR .. "src/osd/modules/output/network.cpp", + MAME_DIR .. "src/osd/modules/output/win32_output.cpp", + MAME_DIR .. "src/osd/modules/output/win32_output.h", MAME_DIR .. "src/osd/modules/ipc/tcp_connection.cpp", MAME_DIR .. "src/osd/modules/ipc/tcp_connection.h", MAME_DIR .. "src/osd/modules/ipc/tcp_server.cpp", diff --git a/scripts/src/osd/windows.lua b/scripts/src/osd/windows.lua index b4be2dc7ce4..8b48b8f0771 100644 --- a/scripts/src/osd/windows.lua +++ b/scripts/src/osd/windows.lua @@ -259,3 +259,35 @@ project ("ocore_" .. _OPTIONS["osd"]) MAME_DIR .. "src/osd/modules/file/winsocket.cpp", MAME_DIR .. "src/osd/modules/lib/osdlib_win32.cpp", } + + + +-------------------------------------------------- +-- ledutil +-------------------------------------------------- + +if _OPTIONS["with-tools"] then + project("ledutil") + uuid ("061293ca-7290-44ac-b2b5-5913ae8dc9c0") + kind "ConsoleApp" + + flags { + "Symbols", -- always include minimum symbols for executables + } + + if _OPTIONS["SEPARATE_BIN"]~="1" then + targetdir(MAME_DIR) + end + + links { + "ocore_" .. _OPTIONS["osd"], + } + + includedirs { + MAME_DIR .. "src/osd", + } + + files { + MAME_DIR .. "src/osd/windows/ledutil.cpp", + } +end diff --git a/src/emu/output.cpp b/src/emu/output.cpp index b905c45d70b..026e5f84ad0 100644 --- a/src/emu/output.cpp +++ b/src/emu/output.cpp @@ -9,6 +9,7 @@ #include "emu.h" #include "coreutil.h" +#include "modules/output/output_module.h" //************************************************************************** // OUTPUT MANAGER @@ -210,10 +211,10 @@ void output_manager::set_notifier(const char *outname, output_notifier_func call notifier for all outputs -------------------------------------------------*/ -void output_manager::notify_all(output_notifier_func callback, void *param) +void output_manager::notify_all(output_module *module) { for (auto &item : m_itemtable) - (*callback)(item.second.name.c_str(), item.second.value, param); + module->notify(item.second.name.c_str(), item.second.value); } diff --git a/src/emu/output.h b/src/emu/output.h index e83399aa4b1..69c5bc5b402 100644 --- a/src/emu/output.h +++ b/src/emu/output.h @@ -21,6 +21,7 @@ TYPE DEFINITIONS ***************************************************************************/ +class output_module; typedef void (*output_notifier_func)(const char *outname, INT32 value, void *param); // ======================> output_manager @@ -72,7 +73,7 @@ public: void set_notifier(const char *outname, output_notifier_func callback, void *param); // set a notifier on a particular output, or globally if nullptr - void notify_all(output_notifier_func callback, void *param); + void notify_all(output_module *module); // map a name to a unique ID UINT32 name_to_id(const char *outname); diff --git a/src/osd/modules/lib/osdobj_common.cpp b/src/osd/modules/lib/osdobj_common.cpp index a709a9a8c52..9ba54892f51 100644 --- a/src/osd/modules/lib/osdobj_common.cpp +++ b/src/osd/modules/lib/osdobj_common.cpp @@ -255,6 +255,7 @@ void osd_common_t::register_options() REGISTER_MODULE(m_mod_man, OUTPUT_NONE); REGISTER_MODULE(m_mod_man, OUTPUT_CONSOLE); REGISTER_MODULE(m_mod_man, OUTPUT_NETWORK); + REGISTER_MODULE(m_mod_man, OUTPUT_WIN32); // after initialization we know which modules are supported @@ -643,6 +644,7 @@ void osd_common_t::init_subsystems() m_midi = select_module_options<midi_module *>(options(), OSD_MIDI_PROVIDER); m_output = select_module_options<output_module *>(options(), OSD_OUTPUT_PROVIDER); + m_output->set_machine(&machine()); machine().output().set_notifier(nullptr, output_notifier_callback, this); m_mod_man.init(options()); diff --git a/src/osd/modules/output/output_module.h b/src/osd/modules/output/output_module.h index 81fcaae2cce..f3461d3e1f6 100644 --- a/src/osd/modules/output/output_module.h +++ b/src/osd/modules/output/output_module.h @@ -20,11 +20,16 @@ class output_module { public: - output_module() { } + output_module(): m_machine(nullptr) { } virtual ~output_module() { } virtual void notify(const char *outname, INT32 value) = 0; + + void set_machine(running_machine *machine) { m_machine = machine; }; + running_machine &machine() const { return *m_machine; } +private: + running_machine *m_machine; }; #endif /* OUTPUT_MODULE_H_ */ diff --git a/src/osd/modules/output/win32_output.cpp b/src/osd/modules/output/win32_output.cpp new file mode 100644 index 00000000000..419a1dc1692 --- /dev/null +++ b/src/osd/modules/output/win32_output.cpp @@ -0,0 +1,336 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +//============================================================ +// +// output.c - Win32 implementation of MAME output routines +// +//============================================================ + +#if defined(OSD_WINDOWS) + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +// MAME headers +#include "emu.h" +#include "winmain.h" + +#include "winutil.h" +#include "win32_output.h" + + +//============================================================ +// CONSTANTS +//============================================================ + +// window styles +#define WINDOW_STYLE WS_OVERLAPPEDWINDOW +#define WINDOW_STYLE_EX 0 + + + +//============================================================ +// TYPEDEFS +//============================================================ + +struct registered_client +{ + registered_client * next; // next client in the list + LPARAM id; // client-specified ID + HWND hwnd; // client HWND + running_machine * machine; +}; + + +// message IDs +static UINT om_mame_start; +static UINT om_mame_stop; +static UINT om_mame_update_state; +static UINT om_mame_register_client; +static UINT om_mame_unregister_client; +static UINT om_mame_get_id_string; + + + +//============================================================ +// FUNCTION PROTOTYPES +//============================================================ + +static LRESULT CALLBACK output_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam); + +class output_win32 : public osd_module, public output_module +{ +public: + output_win32() + : osd_module(OSD_OUTPUT_PROVIDER, "windows"), output_module() + { + } + virtual ~output_win32() { } + + virtual int init(const osd_options &options) override; + virtual void exit() override; + + // output_module + + virtual void notify(const char *outname, INT32 value) override; + + int create_window_class(void); + LRESULT register_client(HWND hwnd, LPARAM id); + LRESULT unregister_client(HWND hwnd, LPARAM id); + LRESULT send_id_string(HWND hwnd, LPARAM id); + +private: + // our HWND + HWND m_output_hwnd; + + // client list + registered_client * m_clientlist; + +}; + + +//============================================================ +// output_init +//============================================================ + +int output_win32::init(const osd_options &options) +{ + int result; + + // reset globals + m_clientlist = nullptr; + + // create our window class + result = create_window_class(); + assert(result == 0); + (void)result; // to silence gcc 4.6 + + // create a window + m_output_hwnd = CreateWindowEx( + WINDOW_STYLE_EX, + OUTPUT_WINDOW_CLASS, + OUTPUT_WINDOW_NAME, + WINDOW_STYLE, + 0, 0, + 1, 1, + nullptr, + nullptr, + GetModuleHandleUni(), + nullptr); + assert(m_output_hwnd != nullptr); + + // set a pointer to the running machine + SetWindowLongPtr(m_output_hwnd, GWLP_USERDATA, (LONG_PTR)this); + + // allocate message ids + om_mame_start = RegisterWindowMessage(OM_MAME_START); + assert(om_mame_start != 0); + om_mame_stop = RegisterWindowMessage(OM_MAME_STOP); + assert(om_mame_stop != 0); + om_mame_update_state = RegisterWindowMessage(OM_MAME_UPDATE_STATE); + assert(om_mame_update_state != 0); + + om_mame_register_client = RegisterWindowMessage(OM_MAME_REGISTER_CLIENT); + assert(om_mame_register_client != 0); + om_mame_unregister_client = RegisterWindowMessage(OM_MAME_UNREGISTER_CLIENT); + assert(om_mame_unregister_client != 0); + om_mame_get_id_string = RegisterWindowMessage(OM_MAME_GET_ID_STRING); + assert(om_mame_get_id_string != 0); + + // broadcast a startup message + PostMessage(HWND_BROADCAST, om_mame_start, (WPARAM)m_output_hwnd, 0); + + return 0; +} + + +//============================================================ +// output_exit +//============================================================ + +void output_win32::exit() +{ + // free all the clients + while (m_clientlist != nullptr) + { + registered_client *temp = m_clientlist; + m_clientlist = temp->next; + global_free(temp); + } + + // broadcast a shutdown message + PostMessage(HWND_BROADCAST, om_mame_stop, (WPARAM)m_output_hwnd, 0); +} + + +//============================================================ +// create_window_class +//============================================================ + +int output_win32::create_window_class(void) +{ + static UINT8 classes_created = FALSE; + + /* only do this once */ + if (!classes_created) + { + WNDCLASS wc = { 0 }; + + // initialize the description of the window class + wc.lpszClassName = OUTPUT_WINDOW_CLASS; + wc.hInstance = GetModuleHandleUni(); + wc.lpfnWndProc = output_window_proc; + + UnregisterClass(wc.lpszClassName, wc.hInstance); + + // register the class; fail if we can't + if (!RegisterClass(&wc)) + return 1; + classes_created = TRUE; + } + + return 0; +} + + +//============================================================ +// output_window_proc +//============================================================ + +static LRESULT CALLBACK output_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam) +{ + LONG_PTR ptr = GetWindowLongPtr(wnd, GWLP_USERDATA); + output_win32 &output = *(output_win32 *)ptr; + + // register a new client + if (message == om_mame_register_client) + return output.register_client((HWND)wparam, lparam); + + // unregister a client + else if (message == om_mame_unregister_client) + return output.unregister_client((HWND)wparam, lparam); + + // get a string for an ID + else if (message == om_mame_get_id_string) + return output.send_id_string((HWND)wparam, lparam); + + else + return DefWindowProc(wnd, message, wparam, lparam); +} + + +//============================================================ +// register_client +//============================================================ + +LRESULT output_win32::register_client(HWND hwnd, LPARAM id) +{ + registered_client **client; + + // find the end of the list; if we find ourself already registered, + // return 1 + for (client = &m_clientlist; *client != nullptr; client = &(*client)->next) + if ((*client)->id == id) + { + (*client)->hwnd = hwnd; + machine().output().notify_all(this); + return 1; + } + + // add us to the end + *client = global_alloc(registered_client); + (*client)->next = nullptr; + (*client)->id = id; + (*client)->hwnd = hwnd; + (*client)->machine = &machine(); + + // request a notification for all outputs + machine().output().notify_all(this); + return 0; +} + + +//============================================================ +// unregister_client +//============================================================ + +LRESULT output_win32::unregister_client(HWND hwnd, LPARAM id) +{ + registered_client **client; + int found = FALSE; + + // find any matching IDs in the list and remove them + for (client = &m_clientlist; *client != nullptr; client = &(*client)->next) + if ((*client)->id == id) + { + registered_client *temp = *client; + *client = (*client)->next; + global_free(temp); + found = TRUE; + break; + } + + // return an error if not found + return found ? 0 : 1; +} + + +//============================================================ +// send_id_string +//============================================================ + +LRESULT output_win32::send_id_string(HWND hwnd, LPARAM id) +{ + COPYDATASTRUCT copydata; + const char *name; + int datalen; + + // id 0 is the name of the game + if (id == 0) + name = machine().system().name; + else + name = machine().output().id_to_name(id); + + // a NULL name is an empty string + if (name == nullptr) + name = ""; + + // allocate memory for the message + datalen = sizeof(copydata_id_string) + strlen(name) + 1; + dynamic_buffer buffer(datalen); + copydata_id_string *temp = (copydata_id_string *)&buffer[0]; + temp->id = id; + strcpy(temp->string, name); + + // reply by using SendMessage with WM_COPYDATA + copydata.dwData = COPYDATA_MESSAGE_ID_STRING; + copydata.cbData = datalen; + copydata.lpData = temp; + SendMessage(hwnd, WM_COPYDATA, (WPARAM)m_output_hwnd, (LPARAM)©data); + + return 0; +} + + +//============================================================ +// notifier_callback +//============================================================ + +void output_win32::notify(const char *outname, INT32 value) +{ + registered_client *client; + // loop over clients and notify them + for (client = m_clientlist; client != nullptr; client = client->next) + { + PostMessage(client->hwnd, om_mame_update_state, client->machine->output().name_to_id(outname), value); + } +} + + + +#else +MODULE_NOT_SUPPORTED(output_win32, OSD_OUTPUT_PROVIDER, "windows") +#endif + +MODULE_DEFINITION(OUTPUT_WIN32, output_win32) diff --git a/src/osd/modules/output/win32_output.h b/src/osd/modules/output/win32_output.h new file mode 100644 index 00000000000..9d71332b44b --- /dev/null +++ b/src/osd/modules/output/win32_output.h @@ -0,0 +1,86 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +//============================================================ +// +// output.h - Win32 implementation of MAME output routines +// +//============================================================ + +#ifndef __WINDOWS_OUTPUT_H__ +#define __WINDOWS_OUTPUT_H__ + + +//============================================================ +// CONSTANTS +//============================================================ + +// window parameters +#define OUTPUT_WINDOW_CLASS TEXT("MAMEOutput") +#define OUTPUT_WINDOW_NAME TEXT("MAMEOutput") + +// +// These messages are sent by MAME: +// + +// OM_MAME_START: broadcast when MAME initializes +// WPARAM = HWND of MAME's output window +// LPARAM = unused +#define OM_MAME_START TEXT("MAMEOutputStart") + +// OM_MAME_STOP: broadcast when MAME shuts down +// WPARAM = HWND of MAME's output window +// LPARAM = unused +#define OM_MAME_STOP TEXT("MAMEOutputStop") + +// OM_MAME_UPDATE_STATE: sent to registered clients when the state +// of an output changes +// WPARAM = ID of the output +// LPARAM = new value for the output +#define OM_MAME_UPDATE_STATE TEXT("MAMEOutputUpdateState") + + +// +// These messages are sent by external clients to MAME: +// + +// OM_MAME_REGISTER_CLIENT: sent to MAME to register a client +// WPARAM = HWND of client's listener window +// LPARAM = client-specified ID (must be unique) +#define OM_MAME_REGISTER_CLIENT TEXT("MAMEOutputRegister") + +// OM_MAME_UNREGISTER_CLIENT: sent to MAME to unregister a client +// WPARAM = HWND of client's listener window +// LPARAM = client-specified ID (must match registration) +#define OM_MAME_UNREGISTER_CLIENT TEXT("MAMEOutputUnregister") + +// OM_MAME_GET_ID_STRING: requests the string associated with a +// given ID. ID=0 is always the name of the game. Other IDs are +// only discoverable from a OM_MAME_UPDATE_STATE message. The +// result will be sent back as a WM_COPYDATA message with MAME's +// output window as the sender, dwData = the ID of the string, +// and lpData pointing to a NULL-terminated string. +// WPARAM = HWND of client's listener window +// LPARAM = ID you wish to know about +#define OM_MAME_GET_ID_STRING TEXT("MAMEOutputGetIDString") + + +// +// These constants are used to identify WM_COPYDATA messages +// coming from MAME: +// + +#define COPYDATA_MESSAGE_ID_STRING 1 + + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +struct copydata_id_string +{ + UINT32 id; // ID that was requested + char string[1]; // string array containing the data +}; + +#endif /* __WINDOWS_OUTPUT_H__ */ diff --git a/src/osd/windows/ledutil.cpp b/src/osd/windows/ledutil.cpp new file mode 100644 index 00000000000..60427b4ef8e --- /dev/null +++ b/src/osd/windows/ledutil.cpp @@ -0,0 +1,709 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles,Paul Priest +//============================================================ +// +// ledutil.c - Win32 example code that tracks changing +// outputs and updates the keyboard LEDs in response +// +//============================================================ +// +// This is sample code. To use it as a starting point, you +// should do the following: +// +// 1. Change the CLIENT_ID define to something unique. +// +// 2. Change the WINDOW_CLASS and WINDOW_NAME defines to +// something unique. +// +// 3. Delete all the code from the >8 snip 8< comment and +// downward. +// +// 4. Implement the following functions: +// +// output_startup - called at app init time +// output_shutdown - called before the app exits +// output_mame_start - called when MAME starts +// output_mame_stop - called when MAME exits +// output_set_state - called whenever state changes +// +//============================================================ + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <winioctl.h> + +// standard C headers +#include <conio.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// MAME output header file +typedef int running_machine; +#include "osdcomm.h" +#include "modules/output/win32_output.h" + + +//============================================================ +// DEBUGGING +//============================================================ + +// note you need to compile as a console app to have any of +// these printfs show up +#define DEBUG_VERSION 0 + +#if DEBUG_VERSION +#define DEBUG_PRINTF(x) printf x +#else +#define DEBUG_PRINTF(x) +#endif + + +//============================================================ +// CONSTANTS +//============================================================ + +// unique client ID +#define CLIENT_ID (('M' << 24) | ('L' << 16) | ('E' << 8) | ('D' << 0)) + +// LED methods +#define LED_METHOD_PS2 0 +#define LED_METHOD_USB 1 + +// window parameters +#define WINDOW_CLASS TEXT("LEDSample") +#define WINDOW_NAME TEXT("LEDSample") + +// window styles +#define WINDOW_STYLE WS_OVERLAPPEDWINDOW +#define WINDOW_STYLE_EX 0 + +// Define the keyboard indicators. +// (Definitions borrowed from ntddkbd.h) + +#define IOCTL_KEYBOARD_SET_INDICATORS CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0002, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_KEYBOARD_QUERY_TYPEMATIC CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0008, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_KEYBOARD_QUERY_INDICATORS CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0010, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define KEYBOARD_SCROLL_LOCK_ON 1 +#define KEYBOARD_NUM_LOCK_ON 2 +#define KEYBOARD_CAPS_LOCK_ON 4 + + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +struct KEYBOARD_INDICATOR_PARAMETERS +{ + USHORT UnitId; // Unit identifier. + USHORT LedFlags; // LED indicator state. +}; + + +struct id_map_entry +{ + id_map_entry * next; + const char * name; + WPARAM id; +}; + + + +//============================================================ +// GLOBAL VARIABLES +//============================================================ + +static int ledmethod; +static int original_state; +static int current_state; +static int pause_state; +static HANDLE hKbdDev; + +static HWND mame_target; +static HWND listener_hwnd; + +static id_map_entry * idmaplist; + +// message IDs +static UINT om_mame_start; +static UINT om_mame_stop; +static UINT om_mame_update_state; +static UINT om_mame_register_client; +static UINT om_mame_unregister_client; +static UINT om_mame_get_id_string; + + + +//============================================================ +// FUNCTION PROTOTYPES +//============================================================ + +static int create_window_class(void); +static LRESULT CALLBACK listener_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam); +static LRESULT handle_mame_start(WPARAM wparam, LPARAM lparam); +static LRESULT handle_mame_stop(WPARAM wparam, LPARAM lparam); +static LRESULT handle_copydata(WPARAM wparam, LPARAM lparam); +static void reset_id_to_outname_cache(void); +static const char *map_id_to_outname(WPARAM id); +static LRESULT handle_update_state(WPARAM wparam, LPARAM lparam); + +// these functions provide the meat +static void output_startup(const char *commandline); +static void output_mame_start(void); +static void output_set_state(const char *name, INT32 state); +static void output_mame_stop(void); +static void output_shutdown(void); + +static int led_get_state(void); +static void led_set_state(int state); + + +//============================================================ +// main +//============================================================ + +int main(int argc, char *argv[]) +{ + const char *arg = (argc > 1) ? argv[1] : ""; + int exitcode = 1; + HWND otherwnd; + MSG message; + int result; + + // see if there is another instance of us running + otherwnd = FindWindow(WINDOW_CLASS, WINDOW_NAME); + + // if the argument is "-kill", post a close message + if (strcmp(arg, "-kill") == 0) + { + if (otherwnd != nullptr) + PostMessage(otherwnd, WM_QUIT, 0, 0); + return (otherwnd != nullptr) ? 1 : 0; + } + + // if we had another instance, defer to it + if (otherwnd != nullptr) + return 0; + + // call the startup code + output_startup(arg); + + // create our window class + result = create_window_class(); + if (result != 0) + goto error; + + // create a window + listener_hwnd = CreateWindowEx( + WINDOW_STYLE_EX, + WINDOW_CLASS, + WINDOW_NAME, + WINDOW_STYLE, + 0, 0, + 1, 1, + nullptr, + nullptr, + GetModuleHandle(nullptr), + nullptr); + if (listener_hwnd == nullptr) + goto error; + + // allocate message ids + om_mame_start = RegisterWindowMessage(OM_MAME_START); + if (om_mame_start == 0) + goto error; + om_mame_stop = RegisterWindowMessage(OM_MAME_STOP); + if (om_mame_stop == 0) + goto error; + om_mame_update_state = RegisterWindowMessage(OM_MAME_UPDATE_STATE); + if (om_mame_update_state == 0) + goto error; + + om_mame_register_client = RegisterWindowMessage(OM_MAME_REGISTER_CLIENT); + if (om_mame_register_client == 0) + goto error; + om_mame_unregister_client = RegisterWindowMessage(OM_MAME_UNREGISTER_CLIENT); + if (om_mame_unregister_client == 0) + goto error; + om_mame_get_id_string = RegisterWindowMessage(OM_MAME_GET_ID_STRING); + if (om_mame_get_id_string == 0) + goto error; + + // see if MAME is already running + otherwnd = FindWindow(OUTPUT_WINDOW_CLASS, OUTPUT_WINDOW_NAME); + if (otherwnd != nullptr) + handle_mame_start((WPARAM)otherwnd, 0); + + // process messages + while (GetMessage(&message, nullptr, 0, 0)) + { + TranslateMessage(&message); + DispatchMessage(&message); + } + + // reset on the way out if still live + if (mame_target != nullptr) + handle_mame_stop((WPARAM)mame_target, 0); + exitcode = 0; + +error: + // call the shutdown code + output_shutdown(); + + return exitcode; +} + + +//============================================================ +// create_window_class +//============================================================ + +static int create_window_class(void) +{ + static int classes_created = FALSE; + + /* only do this once */ + if (!classes_created) + { + WNDCLASS wc = { 0 }; + + // initialize the description of the window class + wc.lpszClassName = WINDOW_CLASS; + wc.hInstance = GetModuleHandle(nullptr); + wc.lpfnWndProc = listener_window_proc; + + // register the class; fail if we can't + if (!RegisterClass(&wc)) + return 1; + classes_created = TRUE; + } + + return 0; +} + + +//============================================================ +// window_proc +//============================================================ + +static LRESULT CALLBACK listener_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam) +{ + // OM_MAME_START: register ourselves with the new MAME (first instance only) + if (message == om_mame_start) + return handle_mame_start(wparam, lparam); + + // OM_MAME_STOP: no need to unregister, just note that we've stopped caring and reset the LEDs + else if (message == om_mame_stop) + return handle_mame_stop(wparam, lparam); + + // OM_MAME_UPDATE_STATE: update the state of this item if we care + else if (message == om_mame_update_state) + return handle_update_state(wparam, lparam); + + // WM_COPYDATA: extract the string and create an ID map entry + else if (message == WM_COPYDATA) + return handle_copydata(wparam, lparam); + + // everything else is default + else + return DefWindowProc(wnd, message, wparam, lparam); +} + + +//============================================================ +// handle_mame_start +//============================================================ + +static LRESULT handle_mame_start(WPARAM wparam, LPARAM lparam) +{ + DEBUG_PRINTF(("mame_start (%08X)\n", (UINT32)wparam)); + + // make this the targeted version of MAME + mame_target = (HWND)wparam; + + // initialize the LED states + output_mame_start(); + reset_id_to_outname_cache(); + + // register ourselves as a client + PostMessage(mame_target, om_mame_register_client, (WPARAM)listener_hwnd, CLIENT_ID); + + // get the game name + map_id_to_outname(0); + return 0; +} + + +//============================================================ +// handle_mame_stop +//============================================================ + +static LRESULT handle_mame_stop(WPARAM wparam, LPARAM lparam) +{ + DEBUG_PRINTF(("mame_stop (%08X)\n", (UINT32)wparam)); + + // ignore if this is not the instance we care about + if (mame_target != (HWND)wparam) + return 1; + + // clear our target out + mame_target = nullptr; + reset_id_to_outname_cache(); + + // reset the LED states + output_mame_stop(); + return 0; +} + + +//============================================================ +// handle_copydata +//============================================================ + +static LRESULT handle_copydata(WPARAM wparam, LPARAM lparam) +{ + COPYDATASTRUCT *copydata = (COPYDATASTRUCT *)lparam; + copydata_id_string *data = (copydata_id_string *)copydata->lpData; + id_map_entry *entry; + char *string; + + DEBUG_PRINTF(("copydata (%08X)\n", (UINT32)wparam)); + + // ignore requests we don't care about + if (mame_target != (HWND)wparam) + return 1; + + // allocate memory + entry = (id_map_entry *)malloc(sizeof(*entry)); + if (entry == nullptr) + return 0; + + string = (char *)malloc(strlen(data->string) + 1); + if (string == nullptr) + { + free(entry); + return 0; + } + + // if all allocations worked, make a new entry + entry->next = idmaplist; + entry->name = string; + entry->id = data->id; + + // copy the string and hook us into the list + strcpy(string, data->string); + idmaplist = entry; + + DEBUG_PRINTF((" id %d = '%s'\n", (int)entry->id, entry->name)); + + return 0; +} + + +//============================================================ +// reset_id_to_outname_cache +//============================================================ + +static void reset_id_to_outname_cache(void) +{ + // free our ID list + while (idmaplist != nullptr) + { + id_map_entry *temp = idmaplist; + idmaplist = temp->next; + free((void*)temp->name); + free(temp); + } +} + + +//============================================================ +// map_id_to_outname +//============================================================ + +static const char *map_id_to_outname(WPARAM id) +{ + id_map_entry *entry; + + // see if we have an entry in our map + for (entry = idmaplist; entry != nullptr; entry = entry->next) + if (entry->id == id) + return entry->name; + + // no entry yet; we have to ask + SendMessage(mame_target, om_mame_get_id_string, (WPARAM)listener_hwnd, id); + + // now see if we have the entry in our map + for (entry = idmaplist; entry != nullptr; entry = entry->next) + if (entry->id == id) + return entry->name; + + // if not, use an empty string + return ""; +} + + +//============================================================ +// handle_update_state +//============================================================ + +static LRESULT handle_update_state(WPARAM wparam, LPARAM lparam) +{ + DEBUG_PRINTF(("update_state: id=%d state=%d\n", (UINT32)wparam, (UINT32)lparam)); + output_set_state(map_id_to_outname(wparam), lparam); + return 0; +} + + +// +// END BOILERPLATE CODE +// +// ------------------------>8 snip 8<------------------------- +// +// BEGIN LED-SPECIFIC CODE +// + + +//============================================================ +// output_startup +//============================================================ + +static void output_startup(const char *commandline) +{ + // default to PS/2, override if USB is specified as a parameter + ledmethod = LED_METHOD_PS2; + if (commandline != nullptr && strcmp(commandline, "-usb") == 0) + ledmethod = LED_METHOD_USB; + + // output the method + switch (ledmethod) + { + case LED_METHOD_PS2: + DEBUG_PRINTF(("Using PS/2 method\n")); + break; + + case LED_METHOD_USB: + DEBUG_PRINTF(("Using USB method\n")); + break; + } +} + + +//============================================================ +// output_shutdown +//============================================================ + +static void output_shutdown(void) +{ + // nothing to do here +} + + +//============================================================ +// output_mame_start +//============================================================ + +static void output_mame_start(void) +{ + HRESULT error_number; + + // initialize the system based on the method + switch (ledmethod) + { + case LED_METHOD_PS2: + if (!DefineDosDevice(DDD_RAW_TARGET_PATH, TEXT("Kbd"), TEXT("\\Device\\KeyboardClass0"))) + { + error_number = GetLastError(); + fprintf(stderr, "Unable to open the keyboard device. (error %d)\n", (UINT32)error_number); + return; + } + + hKbdDev = CreateFile(TEXT("\\\\.\\Kbd"), GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); + if (hKbdDev == INVALID_HANDLE_VALUE) + { + error_number = GetLastError(); + fprintf(stderr, "Unable to open the keyboard device. (error %d)\n", (UINT32)error_number); + return; + } + break; + } + + // remember the initial LED states + original_state = current_state = led_get_state(); +} + + +//============================================================ +// output_mame_stop +//============================================================ + +static void output_mame_stop(void) +{ + int error_number = 0; + + // restore the initial LED states + led_set_state(original_state); + + switch (ledmethod) + { + case LED_METHOD_PS2: + if (!DefineDosDevice(DDD_REMOVE_DEFINITION, TEXT("Kbd"), nullptr)) + { + error_number = GetLastError(); + fprintf(stderr, "Unable to close the keyboard device. (error %d)\n", error_number); + return; + } + if (!CloseHandle(hKbdDev)) + { + error_number = GetLastError(); + fprintf(stderr, "Unable to close the keyboard device. (error %d)\n", error_number); + return; + } + break; + } +} + + +//============================================================ +// output_set_state +//============================================================ + +static void output_set_state(const char *outname, INT32 state) +{ + // look for pause state + if (strcmp(outname, "pause") == 0) + { + if (state) + { + pause_state = led_get_state(); + led_set_state(original_state); + } + else + { + original_state = led_get_state(); + led_set_state(pause_state); + } + } + // look for LED0/LED1/LED2 states and update accordingly + else if (strcmp(outname, "led0") == 0) + led_set_state((current_state & ~1) | (state & 1)); + else if (strcmp(outname, "led1") == 0) + led_set_state((current_state & ~2) | ((state & 1) << 1)); + else if (strcmp(outname, "led2") == 0) + led_set_state((current_state & ~4) | ((state & 1) << 2)); +} + + +//============================================================ +// led_get_state +//============================================================ + +static int led_get_state(void) +{ + int result = 0; + + switch (ledmethod) + { + case LED_METHOD_USB: + { + BYTE key_states[256]; + + // get the current state + GetKeyboardState(&key_states[0]); + + // set the numlock bit + result |= (key_states[VK_NUMLOCK] & 1); + result |= (key_states[VK_CAPITAL] & 1) << 1; + result |= (key_states[VK_SCROLL] & 1) << 2; + break; + } + + case LED_METHOD_PS2: + { + KEYBOARD_INDICATOR_PARAMETERS OutputBuffer; // Output buffer for DeviceIoControl + ULONG DataLength = sizeof(KEYBOARD_INDICATOR_PARAMETERS); + ULONG ReturnedLength; // Number of bytes returned in output buffer + + // Address first keyboard + OutputBuffer.UnitId = 0; + + DeviceIoControl(hKbdDev, IOCTL_KEYBOARD_QUERY_INDICATORS, + nullptr, 0, + &OutputBuffer, DataLength, + &ReturnedLength, nullptr); + + // Demangle lights to match 95/98 + if (OutputBuffer.LedFlags & KEYBOARD_NUM_LOCK_ON) result |= 0x1; + if (OutputBuffer.LedFlags & KEYBOARD_CAPS_LOCK_ON) result |= 0x2; + if (OutputBuffer.LedFlags & KEYBOARD_SCROLL_LOCK_ON) result |= 0x4; + break; + } + } + + return result; +} + + +//============================================================ +// led_set_state +//============================================================ + +static void led_set_state(int state) +{ + current_state = state; + + switch (ledmethod) + { + case LED_METHOD_USB: + { + static const BYTE vk[3] = { VK_NUMLOCK, VK_CAPITAL, VK_SCROLL }; + BYTE keyState[256]; + int k; + + GetKeyboardState((LPBYTE)&keyState); + for (k = 0; k < 3; k++) + { + if ((((state >> k) & 1) && !(keyState[vk[k]] & 1)) || + (!((state >> k) & 1) && (keyState[vk[k]] & 1))) + { + // Simulate a key press + keybd_event(vk[k], 0x45, KEYEVENTF_EXTENDEDKEY | 0, 0); + + // Simulate a key release + keybd_event(vk[k], 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0); + } + } + + keyState[VK_NUMLOCK] = (keyState[VK_NUMLOCK] & ~1) | ((state >> 0) & 1); + keyState[VK_CAPITAL] = (keyState[VK_CAPITAL] & ~1) | ((state >> 1) & 1); + keyState[VK_SCROLL] = (keyState[VK_SCROLL] & ~1) | ((state >> 2) & 1); + SetKeyboardState(&keyState[0]); + break; + } + + case LED_METHOD_PS2: + { + KEYBOARD_INDICATOR_PARAMETERS InputBuffer; // Input buffer for DeviceIoControl + ULONG DataLength = sizeof(KEYBOARD_INDICATOR_PARAMETERS); + ULONG ReturnedLength; // Number of bytes returned in output buffer + UINT LedFlags=0; + + // Demangle lights to match 95/98 + if (state & 0x1) LedFlags |= KEYBOARD_NUM_LOCK_ON; + if (state & 0x2) LedFlags |= KEYBOARD_CAPS_LOCK_ON; + if (state & 0x4) LedFlags |= KEYBOARD_SCROLL_LOCK_ON; + + // Address first keyboard + InputBuffer.UnitId = 0; + InputBuffer.LedFlags = LedFlags; + DeviceIoControl(hKbdDev, IOCTL_KEYBOARD_SET_INDICATORS, + &InputBuffer, DataLength, + nullptr, 0, + &ReturnedLength, nullptr); + break; + } + } +} |