// license:BSD-3-Clause
// copyright-holders:Aaron Giles, Brad Hughes
//============================================================
//
// input_win32.cpp - Win32 input implementation
//
//============================================================
#include "input_module.h"
#include "modules/osdmodule.h"
#if defined(OSD_WINDOWS)
// standard windows headers
#include <windows.h>
#include <tchar.h>
#undef interface
// MAME headers
#include "emu.h"
#include "strconv.h"
// MAMEOS headers
#include "winmain.h"
#include "window.h"
#include "input_common.h"
#include "input_windows.h"
//============================================================
// win32_keyboard_device
//============================================================
// This device is purely event driven so the implementation is in the module
class win32_keyboard_device : public event_based_device<KeyPressEventArgs>
{
public:
keyboard_state keyboard;
win32_keyboard_device(running_machine& machine, const char *name, const char *id, input_module &module)
: event_based_device(machine, name, id, DEVICE_CLASS_KEYBOARD, module),
keyboard({{0}})
{
}
void reset() override
{
memset(&keyboard, 0, sizeof(keyboard));
}
protected:
void process_event(KeyPressEventArgs &args) override
{
keyboard.state[args.scancode] = args.event_id == INPUT_EVENT_KEYDOWN ? 0x80 : 0x00;
}
};
//============================================================
// keyboard_input_win32 - win32 keyboard input module
//============================================================
class keyboard_input_win32 : public wininput_module
{
private:
public:
keyboard_input_win32()
: wininput_module(OSD_KEYBOARDINPUT_PROVIDER, "win32")
{
}
virtual void input_init(running_machine &machine) override
{
// Add a single win32 keyboard device that we'll monitor using Win32
win32_keyboard_device *devinfo = devicelist()->create_device<win32_keyboard_device>(machine, "Win32 Keyboard 1", "Win32 Keyboard 1", *this);
keyboard_trans_table &table = keyboard_trans_table::instance();
// populate it
for (int keynum = 0; keynum < MAX_KEYS; keynum++)
{
input_item_id itemid = table.map_di_scancode_to_itemid(keynum);
TCHAR keyname[100];
// generate the name
if (GetKeyNameText(((keynum & 0x7f) << 16) | ((keynum & 0x80) << 17), keyname, ARRAY_LENGTH(keyname)) == 0)
_sntprintf(keyname, ARRAY_LENGTH(keyname), TEXT("Scan%03d"), keynum);
std::string name = osd::text::from_tstring(keyname);
// add the item to the device
devinfo->device()->add_item(name.c_str(), itemid, generic_button_get_state<std::uint8_t>, &devinfo->keyboard.state[keynum]);
}
}
bool handle_input_event(input_event eventid, void *eventdata) override
{
if (!input_enabled())
return false;
KeyPressEventArgs *args;
switch (eventid)
{
case INPUT_EVENT_KEYDOWN:
case INPUT_EVENT_KEYUP:
args = static_cast<KeyPressEventArgs*>(eventdata);
devicelist()->for_each_device([args](auto device)
{
auto keyboard = dynamic_cast<win32_keyboard_device*>(device);
if (keyboard != nullptr)
keyboard->queue_events(args, 1);
});
return true;
default:
return false;
}
}
};
//============================================================
// win32_mouse_device
//============================================================
struct win32_mouse_state
{
POINT last_point;
};
class win32_mouse_device : public event_based_device<MouseButtonEventArgs>
{
public:
mouse_state mouse;
win32_mouse_state win32_mouse;
win32_mouse_device(running_machine& machine, const char *name, const char *id, input_module &module)
: event_based_device(machine, name, id, DEVICE_CLASS_MOUSE, module),
mouse({0}),
win32_mouse({{0}})
{
}
void poll() override
{
event_based_device::poll();
CURSORINFO cursor_info = {0};
cursor_info.cbSize = sizeof(CURSORINFO);
GetCursorInfo(&cursor_info);
// We only take over the mouse if the cursor isn't showing
// This should happen anyway in mouse mode
if (!(cursor_info.flags & CURSOR_SHOWING))
{
// We measure the position change from the previously set center position
mouse.lX = (cursor_info.ptScreenPos.x - win32_mouse.last_point.x) * INPUT_RELATIVE_PER_PIXEL;
mouse.lY = (cursor_info.ptScreenPos.y - win32_mouse.last_point.y) * INPUT_RELATIVE_PER_PIXEL;
RECT window_pos = {0};
GetWindowRect(std::static_pointer_cast<win_window_info>(osd_common_t::s_window_list.front())->platform_window(), &window_pos);
// We reset the cursor position to the middle of the window each frame
win32_mouse.last_point.x = window_pos.left + (window_pos.right - window_pos.left) / 2;
win32_mouse.last_point.y = window_pos.top + (window_pos.bottom - window_pos.top) / 2;
SetCursorPos(win32_mouse.last_point.x, win32_mouse.last_point.y);
}
}
void reset() override
{
memset(&mouse, 0, sizeof(mouse));
memset(&win32_mouse, 0, sizeof(win32_mouse));
}
protected:
void process_event(MouseButtonEventArgs &args) override
{
// set the button state
mouse.rgbButtons[args.button] = args.keydown ? 0x80 : 0x00;
// Make sure we have a fresh mouse position on button down
if (args.keydown)
module().poll_if_necessary(machine());
}
};
//============================================================
// mouse_input_win32 - win32 mouse input module
//============================================================
class mouse_input_win32 : public wininput_module
{
public:
mouse_input_win32()
: wininput_module(OSD_MOUSEINPUT_PROVIDER, "win32")
{
}
virtual void input_init(running_machine &machine) override
{
win32_mouse_device *devinfo;
int axisnum, butnum;
if (!input_enabled() || !mouse_enabled())
return;
// allocate a device
devinfo = devicelist()->create_device<win32_mouse_device>(machine, "Win32 Mouse 1", "Win32 Mouse 1", *this);
if (devinfo == nullptr)
return;
// populate the axes
for (axisnum = 0; axisnum < 2; axisnum++)
{
devinfo->device()->add_item(
default_axis_name[axisnum],
static_cast<input_item_id>(ITEM_ID_XAXIS + axisnum),
generic_axis_get_state<LONG>,
&devinfo->mouse.lX + axisnum);
}
// populate the buttons
for (butnum = 0; butnum < 2; butnum++)
{
devinfo->device()->add_item(
default_button_name(butnum),
static_cast<input_item_id>(ITEM_ID_BUTTON1 + butnum),
generic_button_get_state<BYTE>,
&devinfo->mouse.rgbButtons[butnum]);
}
}
bool handle_input_event(input_event eventid, void *eventdata) override
{
if (!input_enabled() || !mouse_enabled() || eventid != INPUT_EVENT_MOUSE_BUTTON)
return false;
auto args = static_cast<MouseButtonEventArgs*>(eventdata);
devicelist()->for_each_device([args](auto device)
{
auto mouse = dynamic_cast<win32_mouse_device*>(device);
if (mouse != nullptr)
mouse->queue_events(args, 1);
});
return true;
}
};
//============================================================
// win32_lightgun_device
//============================================================
class win32_lightgun_device : public event_based_device<MouseButtonEventArgs>
{
private:
BOOL m_lightgun_shared_axis_mode;
int m_gun_index;
public:
mouse_state mouse;
win32_lightgun_device(running_machine& machine, const char *name, const char *id, input_module &module)
: event_based_device(machine, name, id, DEVICE_CLASS_LIGHTGUN, module),
m_lightgun_shared_axis_mode(FALSE),
m_gun_index(0),
mouse({0})
{
m_lightgun_shared_axis_mode = downcast<windows_options &>(machine.options()).dual_lightgun();
// Since we are about to be added to the list, the current size is the zero-based index of where we will be
m_gun_index = downcast<wininput_module&>(module).devicelist()->size();
}
void poll() override
{
event_based_device::poll();
int32_t xpos = 0, ypos = 0;
POINT mousepos;
// if we are using the shared axis hack, the data is updated via Windows messages only
if (m_lightgun_shared_axis_mode)
return;
// get the cursor position and transform into final results
GetCursorPos(&mousepos);
if (!osd_common_t::s_window_list.empty())
{
RECT client_rect;
// get the position relative to the window
HWND hwnd = std::static_pointer_cast<win_window_info>(osd_common_t::s_window_list.front())->platform_window();
GetClientRect(hwnd, &client_rect);
ScreenToClient(hwnd, &mousepos);
// convert to absolute coordinates
xpos = normalize_absolute_axis(mousepos.x, client_rect.left, client_rect.right);
ypos = normalize_absolute_axis(mousepos.y, client_rect.top, client_rect.bottom);
}
// update the X/Y positions
mouse.lX = xpos;
mouse.lY = ypos;
}
void reset() override
{
memset(&mouse, 0, sizeof(mouse));
}
protected:
void process_event(MouseButtonEventArgs &args) override
{
// Are we in shared axis mode?
if (m_lightgun_shared_axis_mode)
{
handle_shared_axis_mode(args);
}
else
{
// In non-shared axis mode, just update the button state
mouse.rgbButtons[args.button] = args.keydown ? 0x80 : 0x00;
}
}
private:
void handle_shared_axis_mode(MouseButtonEventArgs &args)
{
int button = args.button;
// We only handle the first four buttons in shared axis mode
if (button > 3)
return;
// First gun doesn't handle buttons 2 & 3
if (button >= 2 && m_gun_index == 0)
return;
// Second gun doesn't handle buttons 0 & 1
if (button < 2 && m_gun_index == 1)
return;
// Adjust the button if we're the second lightgun
int logical_button = m_gun_index == 1 ? button - 2 : button;
// set the button state
mouse.rgbButtons[logical_button] = args.keydown ? 0x80 : 0x00;
if (args.keydown)
{
RECT client_rect;
POINT mousepos;
// get the position relative to the window
HWND hwnd = std::static_pointer_cast<win_window_info>(osd_common_t::s_window_list.front())->platform_window();
GetClientRect(hwnd, &client_rect);
mousepos.x = args.xpos;
mousepos.y = args.ypos;
ScreenToClient(hwnd, &mousepos);
// convert to absolute coordinates
mouse.lX = normalize_absolute_axis(mousepos.x, client_rect.left, client_rect.right);
mouse.lY = normalize_absolute_axis(mousepos.y, client_rect.top, client_rect.bottom);
}
}
};
//============================================================
// lightgun_input_win32 - win32 lightgun input module
//============================================================
class lightgun_input_win32 : public wininput_module
{
public:
lightgun_input_win32()
: wininput_module(OSD_LIGHTGUNINPUT_PROVIDER, "win32")
{
}
int init_internal() override
{
return 0;
}
virtual void input_init(running_machine &machine) override
{
int max_guns = downcast<windows_options&>(machine.options()).dual_lightgun() ? 2 : 1;
// allocate the lightgun devices
for (int gunnum = 0; gunnum < max_guns; gunnum++)
{
static const char *const gun_names[] = { "Win32 Gun 1", "Win32 Gun 2" };
win32_lightgun_device *devinfo;
int axisnum, butnum;
// allocate a device
devinfo = devicelist()->create_device<win32_lightgun_device>(machine, gun_names[gunnum], gun_names[gunnum], *this);
if (devinfo == nullptr)
break;
// populate the axes
for (axisnum = 0; axisnum < 2; axisnum++)
{
devinfo->device()->add_item(
default_axis_name[axisnum],
static_cast<input_item_id>(ITEM_ID_XAXIS + axisnum),
generic_axis_get_state<LONG>,
&devinfo->mouse.lX + axisnum);
}
// populate the buttons
for (butnum = 0; butnum < 2; butnum++)
{
devinfo->device()->add_item(
default_button_name(butnum),
static_cast<input_item_id>(ITEM_ID_BUTTON1 + butnum),
generic_button_get_state<BYTE>,
&devinfo->mouse.rgbButtons[butnum]);
}
}
}
bool handle_input_event(input_event eventid, void* eventdata) override
{
if (!input_enabled() || !lightgun_enabled() || eventid != INPUT_EVENT_MOUSE_BUTTON)
return false;
auto args = static_cast<MouseButtonEventArgs*>(eventdata);
devicelist()->for_each_device([args](auto device)
{
auto lightgun = dynamic_cast<win32_lightgun_device*>(device);
if (lightgun != nullptr)
lightgun->queue_events(args, 1);
});
return true;
}
};
#else
MODULE_NOT_SUPPORTED(keyboard_input_win32, OSD_KEYBOARDINPUT_PROVIDER, "win32")
MODULE_NOT_SUPPORTED(mouse_input_win32, OSD_MOUSEINPUT_PROVIDER, "win32")
MODULE_NOT_SUPPORTED(lightgun_input_win32, OSD_LIGHTGUNINPUT_PROVIDER, "win32")
#endif
MODULE_DEFINITION(KEYBOARDINPUT_WIN32, keyboard_input_win32)
MODULE_DEFINITION(MOUSEINPUT_WIN32, mouse_input_win32)
MODULE_DEFINITION(LIGHTGUNINPUT_WIN32, lightgun_input_win32)