1127 lines
38 KiB
C
1127 lines
38 KiB
C
/* PIKA - Photo and Image Kooker Application
|
|
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
|
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
|
*
|
|
* Original copyright, applying to most contents (license remains unchanged):
|
|
* Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
|
|
*
|
|
* controller_dx_dinput.c
|
|
* Copyright (C) 2004-2007 Sven Neumann <sven@gimp.org>
|
|
* Michael Natterer <mitch@gimp.org>
|
|
* Tor Lillqvist <tml@iki.fi>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/* When using gcc with the February 2007 version of the DirectX SDK,
|
|
* at least, you need to fix a couple of duplicate typedefs in the
|
|
* DirectX <dinput.h>. Unfortunately I can't include the diff here,
|
|
* both for copyright reasons, and because that would confuse the C
|
|
* lexer even if inside #if 0.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
#define _WIN32_WINNT 0x0501
|
|
#include <windows.h>
|
|
#define DIRECTINPUT_VERSION 0x0800
|
|
#include <dinput.h>
|
|
|
|
#include <gegl.h>
|
|
#include <gtk/gtk.h>
|
|
|
|
#include "libpikaconfig/pikaconfig.h"
|
|
#include "libpikamodule/pikamodule.h"
|
|
#include "libpikawidgets/pikawidgets.h"
|
|
|
|
#define PIKA_ENABLE_CONTROLLER_UNDER_CONSTRUCTION
|
|
#include "libpikawidgets/pikacontroller.h"
|
|
|
|
#include "pikainputdevicestore.h"
|
|
|
|
#include "libpika/libpika-intl.h"
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_DEVICE,
|
|
PROP_DEVICE_STORE
|
|
};
|
|
|
|
#define NUM_EVENTS_PER_BUTTON 3 /* Button click, press and release */
|
|
#define NUM_EVENTS_PER_AXIS 2 /* Axis decrease and increase */
|
|
#define NUM_EVENTS_PER_SLIDER 2 /* Slider decrease and increase */
|
|
#define NUM_EVENTS_PER_POV 3 /* POV view vector X and Y view and return */
|
|
|
|
#define CONTROLLER_TYPE_DX_DINPUT (controller_dx_dinput_get_type ())
|
|
#define CONTROLLER_DX_DINPUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CONTROLLER_TYPE_DX_DINPUT, ControllerDXDInput))
|
|
#define CONTROLLER_DX_DINPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CONTROLLER_TYPE_DX_DINPUT, ControllerDXDInputClass))
|
|
#define CONTROLLER_IS_DX_DINPUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CONTROLLER_TYPE_DX_DINPUT))
|
|
#define CONTROLLER_IS_DX_DINPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CONTROLLER_TYPE_DX_DINPUT))
|
|
|
|
typedef struct _DXDInputSource DXDInputSource;
|
|
typedef struct _ControllerDXDInput ControllerDXDInput;
|
|
typedef struct _ControllerDXDInputClass ControllerDXDInputClass;
|
|
|
|
struct _DXDInputSource
|
|
{
|
|
GSource source;
|
|
ControllerDXDInput *controller;
|
|
};
|
|
|
|
struct _ControllerDXDInput
|
|
{
|
|
PikaController parent_instance;
|
|
|
|
PikaInputDeviceStore *store;
|
|
|
|
LPDIRECTINPUTDEVICE8W didevice8;
|
|
|
|
HANDLE event;
|
|
|
|
GPollFD *pollfd;
|
|
|
|
gint num_buttons;
|
|
gint num_button_events;
|
|
|
|
gint num_axes;
|
|
gint num_axis_events;
|
|
|
|
gint num_sliders;
|
|
gint num_slider_events;
|
|
|
|
gint num_povs;
|
|
gint num_pov_events;
|
|
|
|
gint num_events;
|
|
gchar **event_names;
|
|
gchar **event_blurbs;
|
|
|
|
DIDATAFORMAT *format;
|
|
|
|
guchar *prevdata;
|
|
|
|
gchar *guid;
|
|
GSource *source;
|
|
|
|
/* Variables used by enumeration callbacks only*/
|
|
gint bei, bi, aei, ai, sei, si, pei, pi;
|
|
};
|
|
|
|
struct _ControllerDXDInputClass
|
|
{
|
|
PikaControllerClass parent_class;
|
|
};
|
|
|
|
|
|
GType controller_dx_dinput_get_type (void);
|
|
|
|
static void dx_dinput_dispose (GObject *object);
|
|
static void dx_dinput_finalize (GObject *object);
|
|
static void dx_dinput_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static void dx_dinput_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
|
|
static gint dx_dinput_get_n_events (PikaController *controller);
|
|
static const gchar * dx_dinput_get_event_name (PikaController *controller,
|
|
gint event_id);
|
|
static const gchar * dx_dinput_get_event_blurb (PikaController *controller,
|
|
gint event_id);
|
|
|
|
static void dx_dinput_device_changed (ControllerDXDInput *controller,
|
|
const gchar *guid);
|
|
static gboolean dx_dinput_set_device (ControllerDXDInput *controller,
|
|
const gchar *guid);
|
|
|
|
|
|
static const PikaModuleInfo dx_dinput_info =
|
|
{
|
|
PIKA_MODULE_ABI_VERSION,
|
|
N_("DirectX DirectInput event controller"),
|
|
"Tor Lillqvist <tml@iki.fi>",
|
|
"v0.1",
|
|
"(c) 2004-2007, released under the GPL",
|
|
"2004-2007"
|
|
};
|
|
|
|
|
|
G_DEFINE_DYNAMIC_TYPE (ControllerDXDInput, controller_dx_dinput,
|
|
PIKA_TYPE_CONTROLLER)
|
|
|
|
|
|
G_MODULE_EXPORT const PikaModuleInfo *
|
|
pika_module_query (GTypeModule *module)
|
|
{
|
|
return &dx_dinput_info;
|
|
}
|
|
|
|
G_MODULE_EXPORT gboolean
|
|
pika_module_register (GTypeModule *module)
|
|
{
|
|
pika_input_device_store_register_types (module);
|
|
controller_dx_dinput_register_type (module);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
controller_dx_dinput_class_init (ControllerDXDInputClass *klass)
|
|
{
|
|
PikaControllerClass *controller_class = PIKA_CONTROLLER_CLASS (klass);
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->dispose = dx_dinput_dispose;
|
|
object_class->finalize = dx_dinput_finalize;
|
|
object_class->get_property = dx_dinput_get_property;
|
|
object_class->set_property = dx_dinput_set_property;
|
|
|
|
g_object_class_install_property (object_class, PROP_DEVICE,
|
|
g_param_spec_string ("device",
|
|
_("Device:"),
|
|
_("The device to read DirectInput events from."),
|
|
NULL,
|
|
PIKA_CONFIG_PARAM_FLAGS));
|
|
g_object_class_install_property (object_class, PROP_DEVICE_STORE,
|
|
g_param_spec_object ("device-values",
|
|
NULL, NULL,
|
|
PIKA_TYPE_INPUT_DEVICE_STORE,
|
|
G_PARAM_READABLE));
|
|
|
|
controller_class->name = _("DirectX DirectInput");
|
|
controller_class->help_id = "pika-controller-directx-directinput";
|
|
controller_class->icon_name = PIKA_ICON_CONTROLLER_LINUX_INPUT;
|
|
|
|
controller_class->get_n_events = dx_dinput_get_n_events;
|
|
controller_class->get_event_name = dx_dinput_get_event_name;
|
|
controller_class->get_event_blurb = dx_dinput_get_event_blurb;
|
|
}
|
|
|
|
static void
|
|
controller_dx_dinput_class_finalize (ControllerDXDInputClass *klass)
|
|
{
|
|
}
|
|
|
|
static void
|
|
controller_dx_dinput_init (ControllerDXDInput *controller)
|
|
{
|
|
controller->store = pika_input_device_store_new ();
|
|
|
|
if (controller->store)
|
|
{
|
|
g_signal_connect_swapped (controller->store, "device-added",
|
|
G_CALLBACK (dx_dinput_device_changed),
|
|
controller);
|
|
g_signal_connect_swapped (controller->store, "device-removed",
|
|
G_CALLBACK (dx_dinput_device_changed),
|
|
controller);
|
|
}
|
|
}
|
|
|
|
static void
|
|
dx_dinput_dispose (GObject *object)
|
|
{
|
|
ControllerDXDInput *controller = CONTROLLER_DX_DINPUT (object);
|
|
|
|
dx_dinput_set_device (controller, NULL);
|
|
|
|
G_OBJECT_CLASS (controller_dx_dinput_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
dx_dinput_finalize (GObject *object)
|
|
{
|
|
ControllerDXDInput *controller = CONTROLLER_DX_DINPUT (object);
|
|
|
|
if (controller->source != NULL)
|
|
{
|
|
g_source_remove_poll (controller->source, controller->pollfd);
|
|
g_source_remove (g_source_get_id (controller->source));
|
|
g_source_unref (controller->source);
|
|
}
|
|
|
|
if (controller->didevice8)
|
|
IDirectInputDevice8_SetEventNotification (controller->didevice8, NULL);
|
|
|
|
if (controller->format != NULL)
|
|
{
|
|
g_free (controller->format->rgodf);
|
|
g_free (controller->format);
|
|
controller->format = NULL;
|
|
}
|
|
|
|
g_free (controller->prevdata);
|
|
controller->prevdata = NULL;
|
|
|
|
if (controller->event != NULL)
|
|
{
|
|
CloseHandle (controller->event);
|
|
controller->event = 0;
|
|
}
|
|
|
|
if (controller->store)
|
|
{
|
|
g_object_unref (controller->store);
|
|
controller->store = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (controller_dx_dinput_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
dx_dinput_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ControllerDXDInput *controller = CONTROLLER_DX_DINPUT (object);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_DEVICE:
|
|
dx_dinput_set_device (controller, g_value_get_string (value));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dx_dinput_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ControllerDXDInput *controller = CONTROLLER_DX_DINPUT (object);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_DEVICE:
|
|
g_value_set_string (value, controller->guid);
|
|
break;
|
|
case PROP_DEVICE_STORE:
|
|
g_value_set_object (value, controller->store);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static gint
|
|
dx_dinput_get_n_events (PikaController *controller)
|
|
{
|
|
ControllerDXDInput *cdxdi = (ControllerDXDInput *) controller;
|
|
|
|
return cdxdi->num_events;
|
|
}
|
|
|
|
static const gchar *
|
|
dx_dinput_get_event_name (PikaController *controller,
|
|
gint event_id)
|
|
{
|
|
ControllerDXDInput *cdxdi = (ControllerDXDInput *) controller;
|
|
|
|
if (event_id < 0 || event_id >= cdxdi->num_events)
|
|
return NULL;
|
|
|
|
return cdxdi->event_names[event_id];
|
|
}
|
|
|
|
static const gchar *
|
|
dx_dinput_get_event_blurb (PikaController *controller,
|
|
gint event_id)
|
|
{
|
|
ControllerDXDInput *cdxdi = (ControllerDXDInput *) controller;
|
|
|
|
if (event_id < 0 || event_id >= cdxdi->num_events)
|
|
return NULL;
|
|
|
|
return cdxdi->event_blurbs[event_id];
|
|
}
|
|
|
|
static BOOL CALLBACK
|
|
count_objects (const DIDEVICEOBJECTINSTANCEW *doi,
|
|
void *user_data)
|
|
{
|
|
ControllerDXDInput *controller = (ControllerDXDInput *) user_data;
|
|
HRESULT hresult;
|
|
DIPROPRANGE range;
|
|
|
|
if (doi->dwType & DIDFT_AXIS)
|
|
{
|
|
/* Set reported range to physical range, not to give upper
|
|
* level software any false impression of higher accuracy.
|
|
*/
|
|
|
|
range.diph.dwSize = sizeof (DIPROPRANGE);
|
|
range.diph.dwHeaderSize = sizeof (DIPROPHEADER);
|
|
range.diph.dwObj = doi->dwType;
|
|
range.diph.dwHow = DIPH_BYID;
|
|
|
|
if (FAILED ((hresult = IDirectInputDevice8_GetProperty (controller->didevice8,
|
|
DIPROP_PHYSICALRANGE,
|
|
&range.diph))))
|
|
g_warning ("IDirectInputDevice8::GetProperty failed: %s",
|
|
g_win32_error_message (hresult));
|
|
else
|
|
{
|
|
if (FAILED ((hresult = IDirectInputDevice8_SetProperty (controller->didevice8,
|
|
DIPROP_RANGE,
|
|
&range.diph))))
|
|
g_warning ("IDirectInputDevice8::SetProperty failed: %s",
|
|
g_win32_error_message (hresult));
|
|
}
|
|
}
|
|
|
|
if (IsEqualGUID (&doi->guidType, &GUID_Button))
|
|
controller->num_buttons++;
|
|
else if (IsEqualGUID (&doi->guidType, &GUID_XAxis) ||
|
|
IsEqualGUID (&doi->guidType, &GUID_YAxis) ||
|
|
IsEqualGUID (&doi->guidType, &GUID_ZAxis) ||
|
|
IsEqualGUID (&doi->guidType, &GUID_RxAxis) ||
|
|
IsEqualGUID (&doi->guidType, &GUID_RyAxis) ||
|
|
IsEqualGUID (&doi->guidType, &GUID_RzAxis))
|
|
controller->num_axes++;
|
|
else if (IsEqualGUID (&doi->guidType, &GUID_Slider))
|
|
controller->num_sliders++;
|
|
else if (IsEqualGUID (&doi->guidType, &GUID_POV))
|
|
controller->num_povs++;
|
|
|
|
return DIENUM_CONTINUE;
|
|
}
|
|
|
|
static BOOL CALLBACK
|
|
name_objects (const DIDEVICEOBJECTINSTANCEW *doi,
|
|
void *user_data)
|
|
{
|
|
ControllerDXDInput *controller = (ControllerDXDInput *) user_data;
|
|
|
|
if (IsEqualGUID (&doi->guidType, &GUID_Button))
|
|
{
|
|
controller->event_names[controller->bei] = g_strdup_printf ("button-%d", controller->bi);
|
|
controller->event_blurbs[controller->bei] = g_strdup_printf (_("Button %d"), controller->bi);
|
|
controller->bei++;
|
|
controller->event_names[controller->bei] = g_strdup_printf ("button-%d-press", controller->bi);
|
|
controller->event_blurbs[controller->bei] = g_strdup_printf (_("Button %d Press"), controller->bi);
|
|
controller->bei++;
|
|
controller->event_names[controller->bei] = g_strdup_printf ("button-%d-release", controller->bi);
|
|
controller->event_blurbs[controller->bei] = g_strdup_printf (_("Button %d Release"), controller->bi);
|
|
controller->bei++;
|
|
|
|
g_assert (controller->bi < controller->num_buttons);
|
|
|
|
controller->bi++;
|
|
}
|
|
else if (IsEqualGUID (&doi->guidType, &GUID_XAxis) ||
|
|
IsEqualGUID (&doi->guidType, &GUID_YAxis) ||
|
|
IsEqualGUID (&doi->guidType, &GUID_ZAxis) ||
|
|
IsEqualGUID (&doi->guidType, &GUID_RxAxis) ||
|
|
IsEqualGUID (&doi->guidType, &GUID_RyAxis) ||
|
|
IsEqualGUID (&doi->guidType, &GUID_RzAxis))
|
|
{
|
|
if (IsEqualGUID (&doi->guidType, &GUID_XAxis))
|
|
{
|
|
controller->event_names[controller->aei] = g_strdup ("x-move-left");
|
|
controller->event_blurbs[controller->aei] = g_strdup (_("X Move Left"));
|
|
controller->aei++;
|
|
controller->event_names[controller->aei] = g_strdup ("x-move-right");
|
|
controller->event_blurbs[controller->aei] = g_strdup (_("X Move Right"));
|
|
controller->aei++;
|
|
}
|
|
else if (IsEqualGUID (&doi->guidType, &GUID_YAxis))
|
|
{
|
|
controller->event_names[controller->aei] = g_strdup ("y-move-away");
|
|
controller->event_blurbs[controller->aei] = g_strdup (_("Y Move Away"));
|
|
controller->aei++;
|
|
controller->event_names[controller->aei] = g_strdup ("y-move-near");
|
|
controller->event_blurbs[controller->aei] = g_strdup (_("Y Move Near"));
|
|
controller->aei++;
|
|
}
|
|
else if (IsEqualGUID (&doi->guidType, &GUID_ZAxis))
|
|
{
|
|
controller->event_names[controller->aei] = g_strdup ("z-move-up");
|
|
controller->event_blurbs[controller->aei] = g_strdup (_("Z Move Up"));
|
|
controller->aei++;
|
|
controller->event_names[controller->aei] = g_strdup ("z-move-down");
|
|
controller->event_blurbs[controller->aei] = g_strdup (_("Z Move Down"));
|
|
controller->aei++;
|
|
}
|
|
else if (IsEqualGUID (&doi->guidType, &GUID_RxAxis))
|
|
{
|
|
controller->event_names[controller->aei] = g_strdup ("x-axis-tilt-away");
|
|
controller->event_blurbs[controller->aei] = g_strdup (_("X Axis Tilt Away"));
|
|
controller->aei++;
|
|
controller->event_names[controller->aei] = g_strdup ("x-axis-tilt-near");
|
|
controller->event_blurbs[controller->aei] = g_strdup (_("X Axis Tilt Near"));
|
|
controller->aei++;
|
|
}
|
|
else if (IsEqualGUID (&doi->guidType, &GUID_RyAxis))
|
|
{
|
|
controller->event_names[controller->aei] = g_strdup ("y-axis-tilt-right");
|
|
controller->event_blurbs[controller->aei] = g_strdup (_("Y Axis Tilt Right"));
|
|
controller->aei++;
|
|
controller->event_names[controller->aei] = g_strdup ("y-axis-tilt-left");
|
|
controller->event_blurbs[controller->aei] = g_strdup (_("Y Axis Tilt Left"));
|
|
controller->aei++;
|
|
}
|
|
else if (IsEqualGUID (&doi->guidType, &GUID_RzAxis))
|
|
{
|
|
controller->event_names[controller->aei] = g_strdup ("z-axis-turn-left");
|
|
controller->event_blurbs[controller->aei] = g_strdup (_("Z Axis Turn Left"));
|
|
controller->aei++;
|
|
controller->event_names[controller->aei] = g_strdup ("z-axis-turn-right");
|
|
controller->event_blurbs[controller->aei] = g_strdup (_("Z Axis Turn Right"));
|
|
controller->aei++;
|
|
}
|
|
|
|
g_assert (controller->ai < controller->num_axes);
|
|
|
|
controller->ai++;
|
|
}
|
|
else if (IsEqualGUID (&doi->guidType, &GUID_Slider))
|
|
{
|
|
controller->event_names[controller->sei] = g_strdup_printf ("slider-%d-increase", controller->si);
|
|
controller->event_blurbs[controller->sei] = g_strdup_printf (_("Slider %d Increase"), controller->si);
|
|
controller->sei++;
|
|
controller->event_names[controller->sei] = g_strdup_printf ("slider-%d-decrease", controller->si);
|
|
controller->event_blurbs[controller->sei] = g_strdup_printf (_("Slider %d Decrease"), controller->si);
|
|
controller->sei++;
|
|
|
|
g_assert (controller->si < controller->num_sliders);
|
|
|
|
controller->si++;
|
|
}
|
|
else if (IsEqualGUID (&doi->guidType, &GUID_POV))
|
|
{
|
|
controller->event_names[controller->pei] = g_strdup_printf ("pov-%d-x-view", controller->pi);
|
|
controller->event_blurbs[controller->pei] = g_strdup_printf (_("POV %d X View"), controller->pi);
|
|
controller->pei++;
|
|
controller->event_names[controller->pei] = g_strdup_printf ("pov-%d-y-view", controller->pi);
|
|
controller->event_blurbs[controller->pei] = g_strdup_printf (_("POV %d Y View"), controller->pi);
|
|
controller->pei++;
|
|
controller->event_names[controller->pei] = g_strdup_printf ("pov-%d-x-return", controller->pi);
|
|
controller->event_blurbs[controller->pei] = g_strdup_printf (_("POV %d Return"), controller->pi);
|
|
controller->pei++;
|
|
|
|
g_assert (controller->pi < controller->num_povs);
|
|
|
|
controller->pi++;
|
|
}
|
|
|
|
return DIENUM_CONTINUE;
|
|
}
|
|
|
|
static gboolean
|
|
dx_dinput_get_device_info (ControllerDXDInput *controller,
|
|
GError **error)
|
|
{
|
|
HRESULT hresult;
|
|
|
|
controller->num_buttons =
|
|
controller->num_axes =
|
|
controller->num_sliders =
|
|
controller->num_povs = 0;
|
|
|
|
if (FAILED ((hresult = IDirectInputDevice8_EnumObjects (controller->didevice8,
|
|
count_objects,
|
|
controller,
|
|
DIDFT_AXIS|
|
|
DIDFT_BUTTON|
|
|
DIDFT_POV|
|
|
DIDFT_PSHBUTTON|
|
|
DIDFT_RELAXIS|
|
|
DIDFT_TGLBUTTON))))
|
|
{
|
|
g_set_error (error, PIKA_MODULE_ERROR, PIKA_MODULE_FAILED,
|
|
"IDirectInputDevice8::EnumObjects failed: %s",
|
|
g_win32_error_message (hresult));
|
|
return FALSE;
|
|
}
|
|
|
|
controller->num_button_events = controller->num_buttons*NUM_EVENTS_PER_BUTTON;
|
|
controller->num_axis_events = controller->num_axes*NUM_EVENTS_PER_AXIS;
|
|
controller->num_slider_events = controller->num_sliders*NUM_EVENTS_PER_SLIDER;
|
|
controller->num_pov_events = controller->num_povs*NUM_EVENTS_PER_POV;
|
|
|
|
controller->num_events =
|
|
controller->num_button_events +
|
|
controller->num_axis_events +
|
|
controller->num_slider_events +
|
|
controller->num_pov_events;
|
|
|
|
controller->event_names = g_new (gchar *, controller->num_events);
|
|
controller->event_blurbs = g_new (gchar *, controller->num_events);
|
|
|
|
controller->bi = controller->ai = controller->si = controller->pi = 0;
|
|
|
|
controller->bei = 0;
|
|
controller->aei = controller->bei + controller->num_button_events;
|
|
controller->sei = controller->aei + controller->num_axis_events;
|
|
controller->pei = controller->sei + controller->num_slider_events;
|
|
|
|
if (FAILED ((hresult = IDirectInputDevice8_EnumObjects (controller->didevice8,
|
|
name_objects,
|
|
controller,
|
|
DIDFT_AXIS|
|
|
DIDFT_BUTTON|
|
|
DIDFT_POV|
|
|
DIDFT_PSHBUTTON|
|
|
DIDFT_RELAXIS|
|
|
DIDFT_TGLBUTTON))))
|
|
{
|
|
g_free (controller->event_names);
|
|
g_free (controller->event_blurbs);
|
|
|
|
g_set_error (error, PIKA_MODULE_ERROR, PIKA_MODULE_FAILED,
|
|
"IDirectInputDevice8::EnumObjects failed: %s",
|
|
g_win32_error_message (hresult));
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
g_assert (controller->bei == controller->num_button_events);
|
|
g_assert (controller->aei == controller->bei + controller->num_axis_events);
|
|
g_assert (controller->sei == controller->aei + controller->num_slider_events);
|
|
g_assert (controller->pei == controller->sei + controller->num_pov_events);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
dx_dinput_device_changed (ControllerDXDInput *controller,
|
|
const gchar *guid)
|
|
{
|
|
if (controller->guid && strcmp (guid, controller->guid) == 0)
|
|
{
|
|
dx_dinput_set_device (controller, guid);
|
|
g_object_notify (G_OBJECT (controller), "device");
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
dx_dinput_event_prepare (GSource *source,
|
|
gint *timeout)
|
|
{
|
|
*timeout = -1;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
dx_dinput_event_check (GSource *source)
|
|
{
|
|
DXDInputSource *input = (DXDInputSource *) source;
|
|
|
|
if (WaitForSingleObject (input->controller->event, 0) == WAIT_OBJECT_0)
|
|
{
|
|
ResetEvent (input->controller->event);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
dx_dinput_event_dispatch (GSource *source,
|
|
GSourceFunc callback,
|
|
gpointer user_data)
|
|
{
|
|
ControllerDXDInput * const input = ((DXDInputSource *) source)->controller;
|
|
PikaController *controller = &input->parent_instance;
|
|
const DIDATAFORMAT * const format = input->format;
|
|
const DIOBJECTDATAFORMAT *rgodf = format->rgodf;
|
|
guchar *data;
|
|
gint i;
|
|
PikaControllerEvent cevent = { 0, };
|
|
|
|
data = g_alloca (format->dwDataSize);
|
|
|
|
if (FAILED (IDirectInputDevice8_GetDeviceState (input->didevice8,
|
|
format->dwDataSize,
|
|
data)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
g_object_ref (controller);
|
|
|
|
for (i = 0; i < input->num_buttons; i++)
|
|
{
|
|
if (input->prevdata[rgodf->dwOfs] != data[rgodf->dwOfs])
|
|
{
|
|
if (data[rgodf->dwOfs] & 0x80)
|
|
{
|
|
/* Click event, compatibility with Linux Input */
|
|
cevent.any.type = PIKA_CONTROLLER_EVENT_TRIGGER;
|
|
cevent.any.source = controller;
|
|
cevent.any.event_id = i*NUM_EVENTS_PER_BUTTON;
|
|
pika_controller_event (controller, &cevent);
|
|
|
|
/* Press event */
|
|
cevent.any.event_id = i*NUM_EVENTS_PER_BUTTON + 1;
|
|
pika_controller_event (controller, &cevent);
|
|
}
|
|
else
|
|
{
|
|
/* Release event */
|
|
cevent.any.type = PIKA_CONTROLLER_EVENT_TRIGGER;
|
|
cevent.any.source = controller;
|
|
cevent.any.event_id = i*NUM_EVENTS_PER_BUTTON + 2;
|
|
pika_controller_event (controller, &cevent);
|
|
}
|
|
}
|
|
rgodf++;
|
|
}
|
|
|
|
for (i = 0; i < input->num_axes; i++)
|
|
{
|
|
LONG *prev = (LONG *)(input->prevdata+rgodf->dwOfs);
|
|
LONG *curr = (LONG *)(data+rgodf->dwOfs);
|
|
|
|
if (ABS (*prev - *curr) > 1)
|
|
{
|
|
cevent.any.type = PIKA_CONTROLLER_EVENT_VALUE;
|
|
cevent.any.source = controller;
|
|
cevent.any.event_id =
|
|
input->num_button_events +
|
|
i*NUM_EVENTS_PER_AXIS;
|
|
g_value_init (&cevent.value.value, G_TYPE_DOUBLE);
|
|
if (*curr - *prev < 0)
|
|
{
|
|
g_value_set_double (&cevent.value.value, *prev - *curr);
|
|
}
|
|
else
|
|
{
|
|
cevent.any.event_id++;
|
|
g_value_set_double (&cevent.value.value, *curr - *prev);
|
|
}
|
|
pika_controller_event (controller, &cevent);
|
|
g_value_unset (&cevent.value.value);
|
|
}
|
|
else
|
|
*curr = *prev;
|
|
rgodf++;
|
|
}
|
|
|
|
for (i = 0; i < input->num_sliders; i++)
|
|
{
|
|
LONG *prev = (LONG *)(input->prevdata+rgodf->dwOfs);
|
|
LONG *curr = (LONG *)(data+rgodf->dwOfs);
|
|
|
|
if (ABS (*prev - *curr) > 1)
|
|
{
|
|
cevent.any.type = PIKA_CONTROLLER_EVENT_VALUE;
|
|
cevent.any.source = controller;
|
|
cevent.any.event_id =
|
|
input->num_button_events +
|
|
input->num_axis_events +
|
|
i*NUM_EVENTS_PER_SLIDER;
|
|
g_value_init (&cevent.value.value, G_TYPE_DOUBLE);
|
|
if (*curr - *prev < 0)
|
|
{
|
|
g_value_set_double (&cevent.value.value, *prev - *curr);
|
|
}
|
|
else
|
|
{
|
|
cevent.any.event_id++;
|
|
g_value_set_double (&cevent.value.value, *curr - *prev);
|
|
}
|
|
pika_controller_event (controller, &cevent);
|
|
g_value_unset (&cevent.value.value);
|
|
}
|
|
else
|
|
*curr = *prev;
|
|
rgodf++;
|
|
}
|
|
|
|
for (i = 0; i < input->num_povs; i++)
|
|
{
|
|
LONG prev = *((LONG *)(input->prevdata+rgodf->dwOfs));
|
|
LONG curr = *((LONG *)(data+rgodf->dwOfs));
|
|
double prevx, prevy;
|
|
double currx, curry;
|
|
|
|
if (prev != curr)
|
|
{
|
|
if (prev == -1)
|
|
{
|
|
prevx = 0.;
|
|
prevy = 0.;
|
|
}
|
|
else
|
|
{
|
|
prevx = sin (prev/36000.*2.*G_PI);
|
|
prevy = cos (prev/36000.*2.*G_PI);
|
|
}
|
|
if (curr == -1)
|
|
{
|
|
currx = 0.;
|
|
curry = 0.;
|
|
}
|
|
else
|
|
{
|
|
currx = sin (curr/36000.*2.*G_PI);
|
|
curry = cos (curr/36000.*2.*G_PI);
|
|
}
|
|
|
|
cevent.any.type = PIKA_CONTROLLER_EVENT_VALUE;
|
|
cevent.any.source = controller;
|
|
cevent.any.event_id =
|
|
input->num_button_events +
|
|
input->num_axis_events +
|
|
input->num_slider_events +
|
|
i*NUM_EVENTS_PER_POV;
|
|
g_value_init (&cevent.value.value, G_TYPE_DOUBLE);
|
|
g_value_set_double (&cevent.value.value, currx - prevx);
|
|
pika_controller_event (controller, &cevent);
|
|
cevent.any.event_id++;
|
|
g_value_set_double (&cevent.value.value, curry - prevy);
|
|
pika_controller_event (controller, &cevent);
|
|
g_value_unset (&cevent.value.value);
|
|
if (curr == -1)
|
|
{
|
|
cevent.any.type = PIKA_CONTROLLER_EVENT_TRIGGER;
|
|
cevent.any.event_id++;
|
|
pika_controller_event (controller, &cevent);
|
|
}
|
|
}
|
|
rgodf++;
|
|
}
|
|
|
|
g_assert (rgodf == format->rgodf + format->dwNumObjs);
|
|
|
|
memmove (input->prevdata, data, format->dwDataSize);
|
|
|
|
g_object_unref (controller);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GSourceFuncs dx_dinput_event_funcs = {
|
|
dx_dinput_event_prepare,
|
|
dx_dinput_event_check,
|
|
dx_dinput_event_dispatch,
|
|
NULL
|
|
};
|
|
|
|
|
|
static void
|
|
dump_data_format (const DIDATAFORMAT *format)
|
|
{
|
|
#ifdef PIKA_UNSTABLE
|
|
gint i;
|
|
|
|
g_print ("dwSize: %ld\n", format->dwSize);
|
|
g_print ("dwObjSize: %ld\n", format->dwObjSize);
|
|
g_print ("dwFlags: ");
|
|
if (format->dwFlags == DIDF_ABSAXIS)
|
|
g_print ("DIDF_ABSAXIS");
|
|
else if (format->dwFlags == DIDF_RELAXIS)
|
|
g_print ("DIDF_RELAXIS");
|
|
else
|
|
g_print ("%#lx", format->dwFlags);
|
|
g_print ("\n");
|
|
g_print ("dwDataSize: %ld\n", format->dwDataSize);
|
|
g_print ("dwNumObjs: %ld\n", format->dwNumObjs);
|
|
|
|
for (i = 0; i < format->dwNumObjs; i++)
|
|
{
|
|
const DIOBJECTDATAFORMAT *oformat = (DIOBJECTDATAFORMAT *) (((char *) format->rgodf) + i*format->dwObjSize);
|
|
unsigned char *guid;
|
|
|
|
g_print ("Object %d:\n", i);
|
|
if (oformat->pguid == NULL)
|
|
g_print (" pguid: NULL\n");
|
|
else
|
|
{
|
|
UuidToString (oformat->pguid, &guid);
|
|
g_print (" pguid: %s\n", guid);
|
|
RpcStringFree (&guid);
|
|
}
|
|
|
|
g_print (" dwOfs: %ld\n", oformat->dwOfs);
|
|
g_print (" dwType: ");
|
|
switch (DIDFT_GETTYPE (oformat->dwType))
|
|
{
|
|
#define CASE(x) case DIDFT_##x: g_print ("DIDFT_"#x); break
|
|
CASE (RELAXIS);
|
|
CASE (ABSAXIS);
|
|
CASE (AXIS);
|
|
CASE (PSHBUTTON);
|
|
CASE (TGLBUTTON);
|
|
CASE (BUTTON);
|
|
CASE (POV);
|
|
CASE (COLLECTION);
|
|
CASE (NODATA);
|
|
#undef CASE
|
|
default: g_print ("%#x", DIDFT_GETTYPE (oformat->dwType));
|
|
}
|
|
g_print (" ");
|
|
if (DIDFT_GETINSTANCE (oformat->dwType) == DIDFT_GETINSTANCE (DIDFT_ANYINSTANCE))
|
|
g_print ("ANYINSTANCE");
|
|
else
|
|
g_print ("#%d", DIDFT_GETINSTANCE (oformat->dwType));
|
|
#define BIT(x) if (oformat->dwType & DIDFT_##x) g_print (" "#x)
|
|
BIT (FFACTUATOR);
|
|
BIT (FFEFFECTTRIGGER);
|
|
BIT (OUTPUT);
|
|
BIT (VENDORDEFINED);
|
|
BIT (ALIAS);
|
|
BIT (OPTIONAL);
|
|
#undef BIT
|
|
g_print ("\n");
|
|
g_print (" dwFlags:");
|
|
switch (oformat->dwFlags & DIDOI_ASPECTACCEL)
|
|
{
|
|
case DIDOI_ASPECTPOSITION: g_print (" DIDOI_ASPECTPOSITION"); break;
|
|
case DIDOI_ASPECTVELOCITY: g_print (" DIDOI_ASPECTVELOCITY"); break;
|
|
case DIDOI_ASPECTACCEL: g_print (" DIDOI_ASPECTACCEL"); break;
|
|
}
|
|
if (oformat->dwFlags & DIDOI_ASPECTFORCE)
|
|
g_print (" DIDOI_ASPECTFORCE");
|
|
g_print ("\n");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static gboolean
|
|
dx_dinput_setup_events (ControllerDXDInput *controller,
|
|
GError **error)
|
|
{
|
|
HRESULT hresult;
|
|
DIPROPDWORD dword;
|
|
gint i, k;
|
|
DXDInputSource *source;
|
|
|
|
if ((controller->event = CreateEvent (NULL, TRUE, FALSE, NULL)) == NULL)
|
|
{
|
|
g_set_error (error, PIKA_MODULE_ERROR, PIKA_MODULE_FAILED,
|
|
"CreateEvent failed: %s",
|
|
g_win32_error_message (GetLastError ()));
|
|
return FALSE;
|
|
}
|
|
|
|
controller->format = g_new (DIDATAFORMAT, 1);
|
|
controller->format->dwSize = sizeof (DIDATAFORMAT);
|
|
controller->format->dwObjSize = sizeof (DIOBJECTDATAFORMAT);
|
|
|
|
dword.diph.dwSize = sizeof (DIPROPDWORD);
|
|
dword.diph.dwHeaderSize = sizeof (DIPROPHEADER);
|
|
dword.diph.dwObj = 0;
|
|
dword.diph.dwHow = DIPH_DEVICE;
|
|
|
|
/* Get the axis mode so we can use the same in the format */
|
|
if (FAILED ((hresult = IDirectInputDevice8_GetProperty (controller->didevice8,
|
|
DIPROP_AXISMODE,
|
|
&dword.diph))))
|
|
{
|
|
g_set_error (error, PIKA_MODULE_ERROR, PIKA_MODULE_FAILED,
|
|
"IDirectInputDevice8::GetParameters failed: %s",
|
|
g_win32_error_message (hresult));
|
|
goto fail0;
|
|
}
|
|
|
|
controller->format->dwFlags = dword.dwData + 1;
|
|
|
|
controller->format->dwNumObjs =
|
|
controller->num_buttons +
|
|
controller->num_axes +
|
|
controller->num_sliders +
|
|
controller->num_povs;
|
|
|
|
controller->format->rgodf = g_new (DIOBJECTDATAFORMAT, controller->format->dwNumObjs);
|
|
|
|
k = 0;
|
|
controller->format->dwDataSize = 0;
|
|
|
|
for (i = 0; i < controller->num_buttons; i++)
|
|
{
|
|
controller->format->rgodf[k].pguid = NULL;
|
|
controller->format->rgodf[k].dwOfs = controller->format->dwDataSize;
|
|
controller->format->rgodf[k].dwType = DIDFT_BUTTON | DIDFT_MAKEINSTANCE (i);
|
|
controller->format->rgodf[k].dwFlags = 0;
|
|
controller->format->dwDataSize += 1;
|
|
k++;
|
|
}
|
|
|
|
controller->format->dwDataSize = 4*((controller->format->dwDataSize + 3)/4);
|
|
|
|
for (i = 0; i < controller->num_axes; i++)
|
|
{
|
|
controller->format->rgodf[k].pguid = NULL;
|
|
controller->format->rgodf[k].dwOfs = controller->format->dwDataSize;
|
|
controller->format->rgodf[k].dwType = DIDFT_AXIS | DIDFT_MAKEINSTANCE (i);
|
|
controller->format->rgodf[k].dwFlags = DIDOI_ASPECTPOSITION;
|
|
controller->format->dwDataSize += 4;
|
|
k++;
|
|
}
|
|
|
|
for (i = 0; i < controller->num_sliders; i++)
|
|
{
|
|
controller->format->rgodf[k].pguid = NULL;
|
|
controller->format->rgodf[k].dwOfs = controller->format->dwDataSize;
|
|
controller->format->rgodf[k].dwType = DIDFT_AXIS | DIDFT_MAKEINSTANCE (i);
|
|
controller->format->rgodf[k].dwFlags = DIDOI_ASPECTPOSITION;
|
|
controller->format->dwDataSize += 4;
|
|
k++;
|
|
}
|
|
|
|
for (i = 0; i < controller->num_povs; i++)
|
|
{
|
|
controller->format->rgodf[k].pguid = NULL;
|
|
controller->format->rgodf[k].dwOfs = controller->format->dwDataSize;
|
|
controller->format->rgodf[k].dwType = DIDFT_POV | DIDFT_MAKEINSTANCE (i);
|
|
controller->format->rgodf[k].dwFlags = 0;
|
|
controller->format->dwDataSize += 4;
|
|
k++;
|
|
}
|
|
|
|
g_assert (k == controller->format->dwNumObjs);
|
|
|
|
controller->format->dwDataSize = 4*((controller->format->dwDataSize + 3)/4);
|
|
controller->prevdata = g_malloc (controller->format->dwDataSize);
|
|
|
|
dump_data_format (controller->format);
|
|
|
|
if (FAILED ((hresult = IDirectInputDevice8_SetDataFormat (controller->didevice8,
|
|
controller->format))))
|
|
{
|
|
g_set_error (error, PIKA_MODULE_ERROR, PIKA_MODULE_FAILED,
|
|
"IDirectInputDevice8::SetDataFormat failed: %s",
|
|
g_win32_error_message (hresult));
|
|
goto fail1;
|
|
}
|
|
|
|
if (FAILED ((hresult = IDirectInputDevice8_SetEventNotification (controller->didevice8,
|
|
controller->event))))
|
|
{
|
|
g_set_error (error, PIKA_MODULE_ERROR, PIKA_MODULE_FAILED,
|
|
"IDirectInputDevice8::SetEventNotification failed: %s",
|
|
g_win32_error_message (hresult));
|
|
goto fail2;
|
|
}
|
|
|
|
if (FAILED ((hresult = IDirectInputDevice8_Acquire (controller->didevice8))))
|
|
{
|
|
g_set_error (error, PIKA_MODULE_ERROR, PIKA_MODULE_FAILED,
|
|
"IDirectInputDevice8::Acquire failed: %s",
|
|
g_win32_error_message (hresult));
|
|
goto fail2;
|
|
}
|
|
|
|
if (FAILED ((hresult = IDirectInputDevice8_GetDeviceState (controller->didevice8,
|
|
controller->format->dwDataSize,
|
|
controller->prevdata))))
|
|
{
|
|
g_set_error (error, PIKA_MODULE_ERROR, PIKA_MODULE_FAILED,
|
|
"IDirectInputDevice8::GetDeviceState failed: %s",
|
|
g_win32_error_message (hresult));
|
|
goto fail2;
|
|
}
|
|
|
|
source = (DXDInputSource *) g_source_new (&dx_dinput_event_funcs,
|
|
sizeof (DXDInputSource));
|
|
source->controller = controller;
|
|
controller->source = (GSource *) source;
|
|
|
|
controller->pollfd = g_new (GPollFD, 1);
|
|
|
|
controller->pollfd->fd = (int) controller->event;
|
|
controller->pollfd->events = G_IO_IN;
|
|
|
|
g_source_add_poll (&source->source, controller->pollfd);
|
|
g_source_attach (&source->source, NULL);
|
|
|
|
return TRUE;
|
|
|
|
fail2:
|
|
IDirectInputDevice8_SetEventNotification (controller->didevice8, NULL);
|
|
fail1:
|
|
g_free (controller->format->rgodf);
|
|
g_free (controller->format);
|
|
controller->format = NULL;
|
|
g_free (controller->prevdata);
|
|
controller->prevdata = NULL;
|
|
fail0:
|
|
CloseHandle (controller->event);
|
|
controller->event = 0;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
dx_dinput_set_device (ControllerDXDInput *controller,
|
|
const gchar *guid)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (controller->guid)
|
|
g_free (controller->guid);
|
|
|
|
controller->guid = g_strdup (guid);
|
|
|
|
g_object_set (controller, "name", _("DirectInput Events"), NULL);
|
|
|
|
if (controller->guid && strlen (controller->guid))
|
|
{
|
|
if (controller->store)
|
|
controller->didevice8 =
|
|
(LPDIRECTINPUTDEVICE8W) pika_input_device_store_get_device_file (controller->store,
|
|
controller->guid);
|
|
}
|
|
else
|
|
{
|
|
g_object_set (controller, "state", _("No device configured"), NULL);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (controller->didevice8)
|
|
{
|
|
if (!dx_dinput_get_device_info (controller, &error) ||
|
|
!dx_dinput_setup_events (controller, &error))
|
|
{
|
|
g_object_set (controller, "state", error->message, NULL);
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
else if (controller->store)
|
|
{
|
|
error = pika_input_device_store_get_error (controller->store);
|
|
|
|
if (error)
|
|
{
|
|
g_object_set (controller, "state", error->message, NULL);
|
|
g_error_free (error);
|
|
}
|
|
else
|
|
{
|
|
g_object_set (controller, "state", _("Device not available"), NULL);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|