Revision: 45552
http://sourceforge.net/p/vice-emu/code/45552
Author: compyx
Date: 2025-03-21 11:04:22 +0000 (Fri, 21 Mar 2025)
Log Message:
-----------
Joystick: reimplement Win32 DirectInput driver
Rewrite DirectInput8 joystick driver. So far the driver enumerates devices and
their axes, buttons and hats (POVs in MS parlance) and their capabilities. The
driver's `open()`, `poll()` and `close()` methods are stubs at the moment.
Also use `DIJOYSTATE2` rather than `DIJOYSTATE` as the data format for the
devices so we can use more axes, buttons and hats if a has them (e.g. XBOX360,
PS4/PS5 controllers).
Modified Paths:
--------------
branches/compyx/joymap-001/vice/src/arch/gtk3/joystickdrv/joystick_win32_directinput.c
branches/compyx/joymap-001/vice/src/joyport/joystick.c
branches/compyx/joymap-001/vice/src/joyport/joystick.h
Modified: branches/compyx/joymap-001/vice/src/arch/gtk3/joystickdrv/joystick_win32_directinput.c
===================================================================
--- branches/compyx/joymap-001/vice/src/arch/gtk3/joystickdrv/joystick_win32_directinput.c 2025-03-20 08:37:23 UTC (rev 45551)
+++ branches/compyx/joymap-001/vice/src/arch/gtk3/joystickdrv/joystick_win32_directinput.c 2025-03-21 11:04:22 UTC (rev 45552)
@@ -33,9 +33,11 @@
#include "lib.h"
#define DIRECTINPUT_VERSION 0x0800
+/* required for any GUID stuff, spectacular linker errors on MSYS2 without it */
#define INITGUID
#include <dinput.h>
+#if 0
typedef struct _JoyAxis {
struct _JoyAxis *next;
DWORD id;
@@ -351,3 +353,331 @@
IDirectInput8_EnumDevices(di, DIDEVTYPE_JOYSTICK, EnumCallBack, NULL, DIEDFL_ALLDEVICES);
return 1;
}
+#endif
+
+/*
+ * New API
+ */
+
+/** \brief Private data object
+ *
+ * Contains arch-specific data of a joystick device.
+ */
+typedef struct joy_priv_s {
+ GUID guid; /**< GUID */
+ LPDIRECTINPUTDEVICE8 didev; /**< DirectInput device instance */
+} joy_priv_t;
+
+
+/** \brief DirectInput8 handle
+ *
+ * Initialized during subsystem init.
+ */
+static LPDIRECTINPUT8 dinput_handle = NULL;
+
+/** \brief Window handle */
+static HINSTANCE window_handle = NULL;
+
+/** \brief Log destination for the driver */
+static log_t winjoy_log = LOG_DEFAULT;
+
+
+/** \brief Allocate and initialize private data object
+ *
+ * \return new private data object
+ */
+static joy_priv_t *joy_priv_new(void)
+{
+ joy_priv_t *priv = lib_malloc(sizeof *priv);
+
+ memset(&priv->guid, 0, sizeof(priv->guid));
+ priv->didev = NULL;
+ return priv;
+}
+
+/** \brief Free private data object and its resources
+ *
+ * Unacquire() and Release() the DirectInput device inside \a priv and free
+ * \a priv.
+ *
+ * \param[in] priv private data object
+ */
+static void joy_priv_free(void *priv)
+{
+ joy_priv_t *pr = priv;
+ if (pr->didev != NULL) {
+ IDirectInputDevice_Unacquire(pr->didev);
+ IDirectInputDevice_Release(pr->didev);
+ }
+ lib_free(priv);
+}
+
+
+/*
+ * Driver methods: NOPs for now
+ */
+static bool win32_joy_open(joystick_device_t *joydev) { return true; }
+static void win32_joy_poll(joystick_device_t *joydev) {}
+static void win32_joy_close(joystick_device_t *joydev) {}
+
+
+/** \brief Driver definition */
+static joystick_driver_t win32_joy_driver = {
+ .open = win32_joy_open,
+ .poll = win32_joy_poll,
+ .close = win32_joy_close,
+ .priv_free = joy_priv_free
+};
+
+
+/** \brief Helper for logging errors with DirectInput
+ *
+ * Log error message in the form "<tt>{msg}</tt>: error 0x<tt>{result}</tt>".
+ *
+ * \param[in] msg message
+ * \param[in] result error code
+ */
+static void log_err_helper(const char *msg, HRESULT result)
+{
+ log_error(winjoy_log, "%s: error 0x%08lx", msg, (unsigned long)result);
+}
+
+/** \brief Size of buffer required to store a GUID as string */
+#define GUIDSTR_BUFSIZE 37
+
+/** \brief Generate string from GUID
+ *
+ * Generate string of \a guid and place in \a buffer.
+ * The \a buffer is expected to be at least #GUIDSTR_BUFSIZE bytes, which allows
+ * for the string and its nul terminator.
+ * Format of the string is "01234567-89AB-CDEF-0123-456789ABCDEF".
+ *
+ * \param[in] guid GUID reference
+ * \param[out] buffer destination of GUID string
+ *
+ * \note There is a \c StringFromGUID2() function in \c combaseapi.h, but
+ * that function expects an OLE string, some kind of wide string thing,
+ * so we just do it ourselves here.
+ */
+static void guid_to_str(const GUID *guid, char *buffer)
+{
+ snprintf(buffer, GUIDSTR_BUFSIZE,
+ "%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
+ guid->Data1,
+ guid->Data2,
+ guid->Data3,
+ guid->Data4[0], guid->Data4[1],
+ guid->Data4[2], guid->Data4[3], guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
+ buffer[GUIDSTR_BUFSIZE - 1] = '\0';
+}
+
+/** \brief Callback for EnumObjects to enumerate axes
+ *
+ * \param[in] lpddoi direct input device object instance
+ * \param[in] pvref joystick device
+ *
+ * \return \c DIENUM_CONTINUE
+ */
+static BOOL enum_axes_callback(LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvref)
+{
+ joystick_device_t *joydev = pvref;
+ joy_priv_t *priv = joydev->priv;
+ joystick_axis_t *axis;
+ DIPROPRANGE range;
+ HRESULT result;
+
+ axis = joystick_axis_new(lpddoi->tszName);
+ axis->code = DIDFT_GETINSTANCE(lpddoi->dwType);
+
+ /* get logical range */
+ range.diph.dwSize = sizeof(DIPROPRANGE);
+ range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
+ range.diph.dwObj = lpddoi->dwType;
+ range.diph.dwHow = DIPH_BYID;
+ result = IDirectInputDevice8_GetProperty(priv->didev,
+ DIPROP_LOGICALRANGE,
+ &range.diph);
+ if (SUCCEEDED(result)) {
+ axis->minimum = (int32_t)range.lMin;
+ axis->maximum = (int32_t)range.lMax;
+ }
+
+ /* add axis and continue */
+ joystick_device_add_axis(joydev, axis);
+ // log_message(winjoy_log, "axis %d: %s (%u): [%d - %d]",
+ // joydev->num_axes - 1, axis->name, axis->code, axis->minimum, axis->maximum);
+ return DIENUM_CONTINUE;
+}
+
+/** \brief Callback for EnumObjects to enumerate buttons
+ *
+ * \param[in] lpddoi direct input device object instance
+ * \param[in] pvref joystick device
+ *
+ * \return \c DIENUM_CONTINUE
+ */
+static BOOL enum_buttons_callback(LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvref)
+{
+ joystick_device_t *joydev = pvref;
+ joystick_button_t *button;
+
+ button = joystick_button_new(lpddoi->tszName);
+ button->code = DIDFT_GETINSTANCE(lpddoi->dwType);
+ joystick_device_add_button(joydev, button);
+ // log_message(winjoy_log, "button %d: %s (%d)", joydev->num_buttons - 1, button->name, button->code);
+ return DIENUM_CONTINUE;
+}
+
+/** \brief Callback for EnumObjects to enumerate hats (POVs)
+ *
+ * \param[in] lpddoi direct input device object instance
+ * \param[in] pvref joystick device
+ *
+ * \return \c DIENUM_CONTINUE
+ */
+static BOOL enum_hats_callback(LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvref)
+{
+ joystick_device_t *joydev = pvref;
+ joystick_hat_t *hat;
+
+ hat = joystick_hat_new(lpddoi->tszName);
+ hat->code = DIDFT_GETINSTANCE(lpddoi->dwType);
+
+ joystick_device_add_hat(joydev, hat);
+ // log_message(winjoy_log, "POV %d: %s (%u)", joydev->num_hats - 1, hat->name, hat->code);
+ return DIENUM_CONTINUE;
+}
+
+/** \brief Callback for IDirectInput8_EnumDevices()
+ *
+ *
+ * \param[in] lpddi DirectInput device instance
+ * \param[in] pvref extra data for callback
+ *
+ * \return \c DIENUM_CONTINUE on success, \c DIENUM_STOP on failure
+ */
+static BOOL enum_devices_callback(LPCDIDEVICEINSTANCE lpddi, LPVOID pvref)
+{
+ LPDIRECTINPUTDEVICE8 didev;
+ DIDEVCAPS caps;
+ joystick_device_t *joydev;
+ joy_priv_t *priv;
+ HRESULT result;
+ int raxes; /* reported number of axes */
+ int rbuttons; /* reported number of buttons */
+ int rpovs; /* reported number of POVs */
+
+ /* create device instance */
+ result = IDirectInput8_CreateDevice(dinput_handle,
+ &lpddi->guidInstance,
+ &didev,
+ NULL);
+ if (result != DI_OK) {
+ log_err_helper("IDirectInput8::CreateDevice() failed", result);
+ return DIENUM_STOP;
+ }
+
+ /* we want the extended DIJOYSTATE2 data format */
+ IDirectInputDevice8_SetDataFormat(didev, &c_dfDIJoystick2);
+ IDirectInputDevice8_SetCooperativeLevel(didev,
+ (HWND)window_handle,
+ DISCL_NONEXCLUSIVE|DISCL_BACKGROUND);
+
+ /* get capabilities of device */
+ caps.dwSize = sizeof(DIDEVCAPS);
+ result = IDirectInputDevice8_GetCapabilities(didev, &caps);
+ if (result != DI_OK) {
+ log_err_helper("IDirectInput8::GetCapabilities() failed", result);
+ return DIENUM_STOP;
+ }
+
+ /* create VICE device instance */
+ joydev = joystick_device_new();
+ joydev->name = lib_strdup(lpddi->tszProductName);
+ joydev->vendor = (uint16_t)(lpddi->guidProduct.Data1 & 0xffff);
+ joydev->product = (uint16_t)((lpddi->guidProduct.Data1 > 16u) & 0xffff);
+ /* don't set num axes etc here, the joystick_device_add_foo() functions
+ * update the number of inputs when called. */
+ raxes = caps.dwAxes;
+ rbuttons = caps.dwButtons;
+ rpovs = caps.dwPOVs;
+
+ /* generate GUID as string for the node member */
+ joydev->node = lib_calloc(GUIDSTR_BUFSIZE, 1);
+ guid_to_str(&lpddi->guidInstance, joydev->node);
+
+ log_message(winjoy_log,
+ "got device \"%s\" [%04x:%04x] GUID: %s, axes: %d, buttons: %d, POVs: %d",
+ joydev->name, (unsigned int)joydev->vendor, (unsigned int)joydev->product,
+ joydev->node, raxes, rbuttons, rpovs);
+
+ /* store arch-specific data */
+ priv = joy_priv_new();
+ priv->guid = lpddi->guidInstance;
+ priv->didev = didev;
+ joydev->priv = priv;
+
+ /* enumerate (absolute) axes */
+ IDirectInputDevice8_EnumObjects(didev,
+ enum_axes_callback,
+ (LPVOID)joydev,
+ DIDFT_ABSAXIS);
+
+ /* enumerate buttons */
+ IDirectInputDevice8_EnumObjects(didev,
+ enum_buttons_callback,
+ (LPVOID)joydev,
+ DIDFT_BUTTON);
+ /* enumerate POVs */
+ IDirectInputDevice8_EnumObjects(didev,
+ enum_hats_callback,
+ (LPVOID)joydev,
+ DIDFT_POV);
+
+ /* TODO: Open device for polling (until the driver's open() method has been
+ * properly implemented and tested. */
+
+ /* register device and continue */
+ joystick_device_register(joydev);
+ return DIENUM_CONTINUE;
+}
+
+
+/** \brief Initialize driver and enumerate and register devices
+ */
+void win32_joystick_init(void)
+{
+ HRESULT result;
+
+ /* initialize DirectInput for use */
+ winjoy_log = log_open("Joystick");
+ log_message(winjoy_log, "Initializing DirectInput8:");
+ window_handle = GetModuleHandle(NULL);
+ result = DirectInput8Create(window_handle,
+ DIRECTINPUT_VERSION,
+ &IID_IDirectInput8,
+ (void *)&dinput_handle,
+ NULL);
+ if (result != DI_OK) {
+ log_error(winjoy_log,
+ "failed to initialize DirectInput8: 0x%08lx, giving up.",
+ (unsigned long)result);
+ log_close(winjoy_log);
+ return;
+ }
+ log_message(winjoy_log, "OK.");
+
+ /* register driver */
+ joystick_driver_register(&win32_joy_driver);
+
+ /* enumerate devices */
+ log_message(winjoy_log, "Obtaining devices list:");
+ result = IDirectInput8_EnumDevices(dinput_handle,
+ DIDEVTYPE_JOYSTICK,
+ enum_devices_callback,
+ NULL,
+ DIEDFL_ATTACHEDONLY);
+ if (result != DI_OK) {
+ log_err_helper("IDirectInput8::EnumDevices() failed", result);
+ }
+}
Modified: branches/compyx/joymap-001/vice/src/joyport/joystick.c
===================================================================
--- branches/compyx/joymap-001/vice/src/joyport/joystick.c 2025-03-20 08:37:23 UTC (rev 45551)
+++ branches/compyx/joymap-001/vice/src/joyport/joystick.c 2025-03-21 11:04:22 UTC (rev 45552)
@@ -2732,8 +2732,7 @@
#elif defined MAC_JOYSTICK
joy_hidlib_init();
#elif defined HAVE_DINPUT
- if (win32_directinput_joystick_init()) {
- }
+ win32_joystick_init();
#elif defined HAVE_SDL_NUMJOYSTICKS
joy_sdl_init();
#endif
Modified: branches/compyx/joymap-001/vice/src/joyport/joystick.h
===================================================================
--- branches/compyx/joymap-001/vice/src/joyport/joystick.h 2025-03-20 08:37:23 UTC (rev 45551)
+++ branches/compyx/joymap-001/vice/src/joyport/joystick.h 2025-03-21 11:04:22 UTC (rev 45552)
@@ -354,7 +354,7 @@
void bsd_joystick_init(void);
void joy_hidlib_init(void);
void joy_hidlib_exit(void);
-int win32_directinput_joystick_init(void);
+void win32_joystick_init(void);
uint16_t get_joystick_value(int index);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|