diff options
Diffstat (limited to 'src/osd/windows/ledutil.cpp')
-rw-r--r-- | src/osd/windows/ledutil.cpp | 710 |
1 files changed, 710 insertions, 0 deletions
diff --git a/src/osd/windows/ledutil.cpp b/src/osd/windows/ledutil.cpp new file mode 100644 index 00000000000..6788fdaf53e --- /dev/null +++ b/src/osd/windows/ledutil.cpp @@ -0,0 +1,710 @@ +// 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 "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 != NULL) + PostMessage(otherwnd, WM_QUIT, 0, 0); + return (otherwnd != NULL) ? 1 : 0; + } + + // if we had another instance, defer to it + if (otherwnd != NULL) + 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, + NULL, + NULL, + GetModuleHandle(NULL), + NULL); + if (listener_hwnd == NULL) + 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 != NULL) + handle_mame_start((WPARAM)otherwnd, 0); + + // process messages + while (GetMessage(&message, NULL, 0, 0)) + { + TranslateMessage(&message); + DispatchMessage(&message); + } + + // reset on the way out if still live + if (mame_target != NULL) + 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(NULL); + 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 = NULL; + 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 == NULL) + return 0; + + string = (char *)malloc(strlen(data->string) + 1); + if (string == NULL) + { + 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", 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 != NULL) + { + 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 != NULL; 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 != NULL; 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 != NULL && 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, NULL, OPEN_EXISTING, 0, NULL); + 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"), NULL)) + { + 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, + NULL, 0, + &OutputBuffer, DataLength, + &ReturnedLength, NULL); + + // 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, + NULL, 0, + &ReturnedLength, NULL); + break; + } + } +} |