summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Miodrag Milanovic <mmicko@gmail.com>2016-06-05 21:11:56 +0200
committer Miodrag Milanovic <mmicko@gmail.com>2016-06-05 21:11:56 +0200
commitdba7c1ac9420110762b635cffe605190399251e5 (patch)
tree0e0598f22988eacd7daf15874972b1f53d5e8dcc
parenteb2617f7ae2d4d052de0cdbb4c7dfff3d8dc2c96 (diff)
Placed back old output system as module "-output windows" need more things cleaned (nw)
-rw-r--r--scripts/src/osd/modules.lua2
-rw-r--r--scripts/src/osd/windows.lua32
-rw-r--r--src/emu/output.cpp5
-rw-r--r--src/emu/output.h3
-rw-r--r--src/osd/modules/lib/osdobj_common.cpp2
-rw-r--r--src/osd/modules/output/output_module.h7
-rw-r--r--src/osd/modules/output/win32_output.cpp336
-rw-r--r--src/osd/modules/output/win32_output.h86
-rw-r--r--src/osd/windows/ledutil.cpp709
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)&copydata);
+
+ 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;
+ }
+ }
+}