Windows works with all buttons. (XInput treats the DPad as buttons, rather than a joystick axis, so code to handle that special case has been added).
This commit is contained in:
@@ -26,18 +26,18 @@ CPMAddPackage(
|
|||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
||||||
file(GLOB_RECURSE jstick_HEADERS "include/*.hpp")
|
file(GLOB_RECURSE jstick_HEADERS "include/*.hpp")
|
||||||
file(GLOB_RECURSE jstick_SRC "src/*.cpp")
|
file(GLOB_RECURSE SHARED_SOURCES "src/shared/*.cpp")
|
||||||
|
file(GLOB_RECURSE WINDOWS_SOURCES "src/platform/windows/*.cpp")
|
||||||
|
file(GLOB_RECURSE LINUX_SOURCES "src/platform/linux/*.cpp")
|
||||||
|
|
||||||
include_directories("include")
|
include_directories("include")
|
||||||
|
|
||||||
if (UNIX AND NOT APPLE)
|
if (UNIX AND NOT APPLE)
|
||||||
add_library(jstick SHARED ${jstick_SRC})
|
add_library(jstick SHARED ${SHARED_SOURCES} ${LINUX_SOURCES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
add_library(jstick STATIC ${jstick_SRC}
|
add_library(jstick STATIC ${SHARED_SOURCES} ${WINDOWS_SOURCES})
|
||||||
src/platform/linux/jstick_linux.cpp
|
|
||||||
src/platform/windows/jstick_xinput.cpp)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_include_directories(jstick PUBLIC
|
target_include_directories(jstick PUBLIC
|
||||||
@@ -48,7 +48,7 @@ target_include_directories(jstick PUBLIC
|
|||||||
|
|
||||||
target_include_directories(jstick PUBLIC ${jstick_SOURCE_DIR}/include)
|
target_include_directories(jstick PUBLIC ${jstick_SOURCE_DIR}/include)
|
||||||
|
|
||||||
#target_link_libraries(jstick PUBLIC Event jlog J3ML)
|
target_link_libraries(jstick PUBLIC Event jlog J3ML)
|
||||||
|
|
||||||
install(TARGETS ${PROJECT_NAME} DESTINATION lib/${PROJECT_NAME})
|
install(TARGETS ${PROJECT_NAME} DESTINATION lib/${PROJECT_NAME})
|
||||||
|
|
||||||
@@ -56,4 +56,4 @@ install(FILES ${jstick_HEADERS} DESTINATION include/${PROJECT_NAME})
|
|||||||
|
|
||||||
add_executable(jstick-test main.cpp)
|
add_executable(jstick-test main.cpp)
|
||||||
|
|
||||||
#target_link_libraries(jstick-test ${PROJECT_NAME})
|
target_link_libraries(jstick-test ${PROJECT_NAME})
|
13
README.md
Normal file
13
README.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
## Josh Stick API
|
||||||
|
|
||||||
|
A single-header, single-namespace, single API for reading controller and joystick device input on Linux and Windows.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
* Windows Support via XInput API
|
||||||
|
* Linux Support via jsdev API
|
||||||
|
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
* Support multiple devices at once.
|
||||||
|
* Note that XInput only supports a maximum of 4 devices.
|
@@ -62,6 +62,11 @@ namespace jstick {
|
|||||||
TheBigOne = 8,
|
TheBigOne = 8,
|
||||||
ThumbL = 9,
|
ThumbL = 9,
|
||||||
ThumbR = 10,
|
ThumbR = 10,
|
||||||
|
|
||||||
|
DPadDown = 11,
|
||||||
|
DPadRight = 12,
|
||||||
|
DPadUp = 13,
|
||||||
|
DPadLeft = 14,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// This event is fired when a new joystick device is detected.
|
/// This event is fired when a new joystick device is detected.
|
||||||
@@ -87,10 +92,14 @@ namespace jstick {
|
|||||||
bool Initialize();
|
bool Initialize();
|
||||||
bool Cleanup();
|
bool Cleanup();
|
||||||
|
|
||||||
|
|
||||||
|
/// @return true if the input system treats the DPad as a Vector2 axis, or false if treated as four buttons.
|
||||||
|
bool GetDPadIsAxisOrButtons();
|
||||||
|
|
||||||
/// Checks the device files for a joystick with the given handle.
|
/// Checks the device files for a joystick with the given handle.
|
||||||
/// @return True if the joystick device file exists.
|
/// @return True if the joystick device file exists.
|
||||||
/// @note Joystick devices are indexed by an incrementing integer ID, on *nix systems.
|
/// @note Joystick devices are indexed by an incrementing integer ID, on *nix systems.
|
||||||
bool JoystickDetected(int jsHandle = 0);
|
bool JoystickDetected(int hwid = 0);
|
||||||
|
|
||||||
/// @return True if there is a connected joystick device with the matching Hardware ID.
|
/// @return True if there is a connected joystick device with the matching Hardware ID.
|
||||||
/// @note jstick supports only one device at a time, currently.
|
/// @note jstick supports only one device at a time, currently.
|
||||||
@@ -105,23 +114,25 @@ namespace jstick {
|
|||||||
void JoystickServiceUpdate();
|
void JoystickServiceUpdate();
|
||||||
void ReadEventLoop();
|
void ReadEventLoop();
|
||||||
|
|
||||||
bool IsButtonDown(const XBoxButton& btn);
|
bool IsButtonDown(const XBoxButton& btn, int hwid = 0);
|
||||||
short GetLeftTrigger();
|
short GetLeftTrigger(int hwid = 0);
|
||||||
short GetRightTrigger();
|
short GetRightTrigger(int hwid = 0);
|
||||||
|
|
||||||
|
|
||||||
Vector2 GetLeftThumbstickAxis(float deadzone = 1e-2f);
|
Vector2 GetLeftThumbstickAxis(float deadzone = 1e-2f, int hwid = 0);
|
||||||
Vector2 GetRightThumbstickAxis(float deadzone = 1e-2f);
|
Vector2 GetRightThumbstickAxis(float deadzone = 1e-2f, int hwid = 0);
|
||||||
Vector2 GetDPadAxis(float deadzone = 1e-2f);
|
Vector2 GetDPadAxis(float deadzone = 1e-2f, int hwid = 0);
|
||||||
|
|
||||||
|
|
||||||
// TODO: Find a better name for the function set that converts -32768/+32768 range to -1/+1
|
// TODO: Find a better name for the function set that converts -32768/+32768 range to -1/+1
|
||||||
// TODO: Because Normalization specifically refers to clamping the Axis Vector to Unit Vector range,
|
// TODO: Because Normalization specifically refers to clamping the Axis Vector to Unit Vector range,
|
||||||
// TODO: And I wan that set of functions as part of the API as well.
|
// TODO: And I wan that set of functions as part of the API as well.
|
||||||
Vector2 GetLeftThumbstickAxisNormalized(float deadzone = 1e-3f);
|
Vector2 GetLeftThumbstickAxisNormalized(float deadzone = 1e-3f, int hwid = 0);
|
||||||
Vector2 GetRightThumbstickAxisNormalized(float deadzone = 1e-3f);
|
Vector2 GetRightThumbstickAxisNormalized(float deadzone = 1e-3f, int hwid = 0);
|
||||||
Vector2 GetDPadAxisNormalized(float deadzone = 1e-3f);
|
Vector2 GetDPadAxisNormalized(float deadzone = 1e-3f, int hwid = 0);
|
||||||
|
|
||||||
float GetLeftTriggerNormalized();
|
float GetLeftTriggerNormalized(int hwid = 0);
|
||||||
float GetRightTriggerNormalized();
|
float GetRightTriggerNormalized(int hwid = 0);
|
||||||
|
|
||||||
|
void Vibrate(int LV = 0, int RV = 0, int hwid = 0);
|
||||||
}
|
}
|
||||||
|
486
main.cpp
486
main.cpp
@@ -1,432 +1,76 @@
|
|||||||
/**
|
|
||||||
* Author: Jason White
|
|
||||||
*
|
|
||||||
* Description:
|
|
||||||
* Reads joystick/gamepad events and displays them.
|
|
||||||
*
|
|
||||||
* Compile:
|
|
||||||
* gcc joystick.c -o joystick
|
|
||||||
*
|
|
||||||
* Run:
|
|
||||||
* ./joystick [/dev/input/jsX]
|
|
||||||
*
|
|
||||||
* See also:
|
|
||||||
* https://www.kernel.org/doc/Documentation/input/joystick-api.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
#include <string>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <filesystem>
|
|
||||||
#include <format>
|
|
||||||
#include <jstick.hpp>
|
#include <jstick.hpp>
|
||||||
#include <fcntl.h>
|
#include <thread>
|
||||||
#include <unistd.h>
|
|
||||||
#include <linux/joystick.h>
|
|
||||||
|
|
||||||
|
|
||||||
enum class XBoxButton : uint8_t
|
|
||||||
{
|
|
||||||
ButtonA = 0,
|
|
||||||
ButtonB = 1,
|
|
||||||
ButtonX = 2,
|
|
||||||
ButtonY = 3,
|
|
||||||
BumperL = 4,
|
|
||||||
BumperR = 5,
|
|
||||||
Back = 6,
|
|
||||||
Start = 7,
|
|
||||||
TheBigOne = 8,
|
|
||||||
ThumbL = 9,
|
|
||||||
ThumbR = 10,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Reads a joystick event from the joystick device.
|
|
||||||
/// @returns 0 on success. Otherwise -1 is returned.
|
|
||||||
int read_event(int fd, struct js_event *event)
|
|
||||||
{
|
|
||||||
ssize_t bytes;
|
|
||||||
|
|
||||||
bytes = read(fd, event, sizeof(*event));
|
|
||||||
|
|
||||||
if (bytes == sizeof(*event))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/// Error, could not read full event.
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @returns the number of axes on the controller or 0 if an error occurs.
|
|
||||||
size_t get_axis_count(int fd)
|
|
||||||
{
|
|
||||||
__u8 axes;
|
|
||||||
|
|
||||||
if (ioctl(fd, JSIOCGAXES, &axes) == -1)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return axes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @returns the number of buttons on the controller or 0 if an error occurs.
|
|
||||||
size_t get_button_count(int fd)
|
|
||||||
{
|
|
||||||
__u8 buttons;
|
|
||||||
if (ioctl(fd, JSIOCGBUTTONS, &buttons) == -1)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return buttons;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Current state of an axis.
|
|
||||||
|
|
||||||
|
|
||||||
struct Axes {
|
|
||||||
short LeftThumbX = 0;
|
|
||||||
short LeftThumbY = 0;
|
|
||||||
short RightThumbX = 0;
|
|
||||||
short RightThumbY = 0;
|
|
||||||
short DPadX = 0;
|
|
||||||
short DPadY = 0;
|
|
||||||
// TODO Left & Right triggers should start at -32767 because 0 would be pressed half way.
|
|
||||||
short LeftTrigger = 0;
|
|
||||||
short RightTrigger = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// Keeps track of the current axis state.
|
|
||||||
/// @note This function assumes that axes are numbered starting from 0, and that
|
|
||||||
/// the X axis is an even number, and the Y axis is an odd number. However, this
|
|
||||||
/// is usually a safe assumption.
|
|
||||||
/// @returns the axis that the event indicated.
|
|
||||||
size_t get_axis_state(struct js_event *event, struct Axes& axes)
|
|
||||||
{
|
|
||||||
/// Mappings on my device, meaning these assumptions are incorrect.
|
|
||||||
// LStick X = 0
|
|
||||||
// LStick Y = 1
|
|
||||||
// LTrigger = 2
|
|
||||||
// RStick X = 3
|
|
||||||
// RStick Y = 4
|
|
||||||
// RTrigger = 5
|
|
||||||
// DirPad X = 6
|
|
||||||
// DirPad Y = 7
|
|
||||||
|
|
||||||
size_t axis = event->number;
|
|
||||||
short value = event->value;
|
|
||||||
|
|
||||||
// X-Box 360 & X-Box One are the same.
|
|
||||||
if (axis == 0) axes.LeftThumbX = value;
|
|
||||||
if (axis == 1) axes.LeftThumbY = value;
|
|
||||||
if (axis == 2) axes.LeftTrigger = value;
|
|
||||||
if (axis == 3) axes.RightThumbX = value;
|
|
||||||
if (axis == 4) axes.RightThumbY = value;
|
|
||||||
if (axis == 5) axes.RightTrigger = value;
|
|
||||||
if (axis == 6) axes.DPadX = value;
|
|
||||||
if (axis == 7) axes.DPadY = value;
|
|
||||||
|
|
||||||
// "Sony PLAYSTATION(R)3 Controller", Has 6 axes.
|
|
||||||
/*
|
|
||||||
// D-PAD UP -> Button 12
|
|
||||||
// D-Pad DOWN -> Button 13
|
|
||||||
// D-Pad LEFT -> Button 14
|
|
||||||
// D-Pad RIGHT -> Button 15
|
|
||||||
if (axis == 0) axes.LeftThumbX = value;
|
|
||||||
if (axis == 1) axes.LeftThumbY = value;
|
|
||||||
if (axis == 3) axes.RightThumbX = value;
|
|
||||||
if (axis == 4) axes.RightThumbY = value;
|
|
||||||
if (axis == 2) axes.LeftTrigger = value;
|
|
||||||
if (axis == 5) axes.RightTrigger = value;
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// Playstation 4
|
|
||||||
/*
|
|
||||||
if (axis == 0) axes.LeftThumbX = value;
|
|
||||||
if (axis == 1) axes.LeftThumbY = value;
|
|
||||||
if (axis == 3) axes.RightThumbX = value;
|
|
||||||
if (axis == 4) axes.RightThumbY = value;
|
|
||||||
if (axis == 2) axes.LeftTrigger = value;
|
|
||||||
if (axis == 5) axes.RightTrigger = value;
|
|
||||||
if (axis == 6) axes.DPadX = value;
|
|
||||||
if (axis == 7) axes.DPadY = value;
|
|
||||||
*/
|
|
||||||
|
|
||||||
// "8BitDo Pro 2" in Direct Input mode. This controller also supports the Xbox mode.
|
|
||||||
/*
|
|
||||||
if (axis == 0) axes.LeftThumbX = value;
|
|
||||||
if (axis == 1) axes.LeftThumbY = value;
|
|
||||||
if (axis == 2) axes.RightThumbX = value;
|
|
||||||
if (axis == 3) axes.RightThumbY = value;
|
|
||||||
if (axis == 4) axes.RightTrigger = value;
|
|
||||||
if (axis == 5) axes.LeftTrigger = value;
|
|
||||||
if (axis == 6) axes.DPadX = value;
|
|
||||||
if (axis == 7) axes.DPadY = value;
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
std::cout << std::format("axis_index: {}, val: {}", axis, value) << std::endl;
|
|
||||||
std::cout << std::format("lthumb: {},{} ltrigger: {}, rthumb: {},{}, rtrigger: {}, dp: {},{}", axes.LeftThumbX, axes.LeftThumbY, axes.LeftTrigger, axes.RightThumbX, axes.RightThumbY, axes.RightTrigger, axes.DPadX, axes.DPadY) << std::endl;
|
|
||||||
|
|
||||||
/*size_t axis = event->number / 2;
|
|
||||||
|
|
||||||
if (axis < 3)
|
|
||||||
{
|
|
||||||
if (event->number % 2 == 0)
|
|
||||||
axes[axis].x = event->value;
|
|
||||||
else
|
|
||||||
axes[axis].y = event->value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return axis;*/
|
|
||||||
return axis;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t get_num_axes(int joystick_handle) {
|
|
||||||
char num_axes;
|
|
||||||
ioctl(joystick_handle, JSIOCGAXES, &num_axes);
|
|
||||||
return num_axes;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string get_joystick_name(int joystick_handle)
|
|
||||||
{
|
|
||||||
char name_buf[128];
|
|
||||||
if (ioctl(joystick_handle, JSIOCGNAME(sizeof(name_buf)), name_buf) < 0)
|
|
||||||
strncpy(name_buf, "Unknown", sizeof(name_buf));
|
|
||||||
return std::string(name_buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
int get_joystick_driver_ver(int joystick_handle)
|
|
||||||
{
|
|
||||||
int version;
|
|
||||||
ioctl(joystick_handle, JSIOCGVERSION, &version);
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool check_for_joystick_device()
|
|
||||||
{
|
|
||||||
return std::filesystem::exists("/dev/input/js0");
|
|
||||||
}
|
|
||||||
|
|
||||||
int js;
|
|
||||||
bool device_open;
|
|
||||||
|
|
||||||
bool connect_device() {
|
|
||||||
js = open("/dev/input/js0", O_NONBLOCK);
|
|
||||||
|
|
||||||
if (js == -1)
|
|
||||||
perror("Could not open joystick");
|
|
||||||
|
|
||||||
|
|
||||||
std::cout << get_joystick_name(js) << std::endl;
|
|
||||||
std::cout << get_joystick_driver_ver(js) << std::endl;
|
|
||||||
std::cout << get_axis_count(js) << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void disconnect_device() {
|
|
||||||
close(js);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ENUM_TO_STRING(var) (#var)
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
std::cout << jstick::NumJoysticksDetected() << std::endl;
|
|
||||||
|
|
||||||
int hwid = jstick::Connect(0);
|
|
||||||
|
|
||||||
if (hwid != -1) {
|
|
||||||
|
|
||||||
std::cout << jstick::GetDeviceName(hwid) << std::endl;
|
|
||||||
|
|
||||||
|
|
||||||
jstick::ButtonPressed += [&](jstick::XBoxButton btn){
|
|
||||||
std::cout << std::format("Button Pressed: {}", (int)btn) << std::endl;
|
|
||||||
};
|
|
||||||
|
|
||||||
jstick::ButtonReleased += [&](jstick::XBoxButton btn) {
|
|
||||||
std::cout << std::format("Button Released: {}", (int)btn) << std::endl;
|
|
||||||
};
|
|
||||||
|
|
||||||
jstick::DPadMoved += [&](Vector2 v) {
|
|
||||||
std::cout << std::format("DPad Moved {},{}", v.x, v.y) << std::endl;
|
|
||||||
};
|
|
||||||
|
|
||||||
jstick::LeftThumbstickMoved += [&](Vector2 v){
|
|
||||||
std::cout << std::format("LThumb Moved {},{}", v.x, v.y) << std::endl;
|
|
||||||
};
|
|
||||||
|
|
||||||
jstick::RightThumbstickMoved += [&](Vector2 v){
|
|
||||||
std::cout << std::format("RThumb Moved {},{}", v.x, v.y) << std::endl;
|
|
||||||
};
|
|
||||||
|
|
||||||
jstick::LeftTriggerMoved += [&] (short val) {
|
|
||||||
std::cout << std::format("LTrigger Moved {}", val) << std::endl;
|
|
||||||
};
|
|
||||||
|
|
||||||
jstick::RightTriggerMoved += [&](short val) {
|
|
||||||
std::cout << std::format("RTrigger Moved {}", val) << std::endl;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
while(true) {
|
|
||||||
jstick::ReadEventLoop();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
const char *device;
|
|
||||||
|
|
||||||
struct js_event event;
|
|
||||||
struct Axes axes = {};
|
|
||||||
size_t axis;
|
|
||||||
|
|
||||||
if (argc > 1)
|
|
||||||
device = argv[1];
|
|
||||||
else
|
|
||||||
device = "/dev/input/js0";
|
|
||||||
|
|
||||||
js = open(device, O_NONBLOCK);
|
|
||||||
|
|
||||||
if (js == -1)
|
|
||||||
perror("Could not open joystick");
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::cout << get_joystick_name(js) << std::endl;
|
|
||||||
std::cout << get_joystick_driver_ver(js) << std::endl;
|
|
||||||
std::cout << get_axis_count(js) << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// This loop will exit if the controller is unplugged.
|
|
||||||
|
|
||||||
bool had = false;
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
bool has_file = check_for_joystick_device();
|
|
||||||
|
|
||||||
if (has_file && !had) {
|
|
||||||
if (js == -1)
|
|
||||||
{
|
|
||||||
std::cout << "Controller plugged in!" << std::endl;
|
|
||||||
js = open(device, O_NONBLOCK);
|
|
||||||
|
|
||||||
if (js == -1)
|
|
||||||
perror("Could not open joystick");
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (!has_file && had) {
|
|
||||||
std::cout << "Controller unplugged!" << std::endl;
|
|
||||||
if (js != -1) {
|
|
||||||
close(js);
|
|
||||||
js = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
had = has_file;
|
|
||||||
|
|
||||||
|
|
||||||
while (read_event(js, &event) == 0)
|
|
||||||
{
|
|
||||||
switch (event.type)
|
|
||||||
{
|
|
||||||
case JS_EVENT_BUTTON:
|
|
||||||
printf("Button %u %s\n", event.number, event.value ? "pressed" : "released");
|
|
||||||
break;
|
|
||||||
case JS_EVENT_AXIS:
|
|
||||||
axis = get_axis_state(&event, axes);
|
|
||||||
//if (axis < 3)
|
|
||||||
//printf("Axis %zu at (%6d, %6d)\n", axis, axes[axis].x, axes[axis].y);
|
|
||||||
break;
|
|
||||||
case JS_EVENT_INIT:
|
|
||||||
default:
|
|
||||||
/// Ignore init events.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
close(js);
|
|
||||||
return 0;
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <windows.h>
|
|
||||||
#include <XInput.h>
|
|
||||||
|
|
||||||
#pragma comment(lib, "XInput.lib")
|
|
||||||
|
|
||||||
class TController
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
int n;
|
|
||||||
XINPUT_STATE state;
|
|
||||||
public:
|
|
||||||
TController(int num)
|
|
||||||
{
|
|
||||||
n = num;
|
|
||||||
}
|
|
||||||
XINPUT_STATE GetState()
|
|
||||||
{
|
|
||||||
ZeroMemory(&state, sizeof(XINPUT_STATE));
|
|
||||||
XInputGetState(n, &state);
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
bool IsConnected()
|
|
||||||
{
|
|
||||||
ZeroMemory(&state, sizeof(XINPUT_STATE));
|
|
||||||
DWORD statenow = XInputGetState(n, &state);
|
|
||||||
|
|
||||||
if(statenow == ERROR_SUCCESS) return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
void vibrate(int LV=0, int RV=0)
|
|
||||||
{
|
|
||||||
XINPUT_VIBRATION vibration;
|
|
||||||
ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION));
|
|
||||||
|
|
||||||
vibration.wLeftMotorSpeed = LV;
|
|
||||||
vibration.wRightMotorSpeed = RV;
|
|
||||||
|
|
||||||
XInputSetState(n, &vibration);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
TController* controller0;
|
|
||||||
|
|
||||||
controller0 = new TController(0);
|
bool success = jstick::Initialize();
|
||||||
if (controller0->IsConnected())
|
|
||||||
{
|
if (!success) {
|
||||||
while (controller0->IsConnected())
|
std::cerr << "Could not initialize jstick!" << std::endl;
|
||||||
{
|
return -1;
|
||||||
if(controller0->GetState().Gamepad.wButtons & XINPUT_GAMEPAD_X)
|
|
||||||
{
|
|
||||||
std::cout << "A.";
|
|
||||||
controller0->vibrate(65535, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!jstick::JoystickDetected()) {
|
||||||
|
std::cerr << "No devices detected!" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (jstick::JoystickDetected()) {
|
||||||
|
jstick::ReadEventLoop();
|
||||||
|
|
||||||
|
if (jstick::IsButtonDown(jstick::XBoxButton::Back))
|
||||||
|
std::cout << "Back Button Pressed" << std::endl;
|
||||||
|
|
||||||
|
if (jstick::IsButtonDown(jstick::XBoxButton::Start))
|
||||||
|
std::cout << "Start Button Pressed" << std::endl;
|
||||||
|
|
||||||
|
if (jstick::IsButtonDown(jstick::XBoxButton::ButtonA))
|
||||||
|
std::cout << "A Button Pressed" << std::endl;
|
||||||
|
|
||||||
|
if (jstick::IsButtonDown(jstick::XBoxButton::ButtonB))
|
||||||
|
std::cout << "B Button Pressed" << std::endl;
|
||||||
|
|
||||||
|
if (jstick::IsButtonDown(jstick::XBoxButton::ButtonX))
|
||||||
|
std::cout << "X Button Pressed" << std::endl;
|
||||||
|
|
||||||
|
if (jstick::IsButtonDown(jstick::XBoxButton::ButtonY))
|
||||||
|
std::cout << "Y Button Pressed" << std::endl;
|
||||||
|
|
||||||
|
if (jstick::IsButtonDown(jstick::XBoxButton::BumperL))
|
||||||
|
std::cout << "Left Bumper Pressed" << std::endl;
|
||||||
|
|
||||||
|
if (jstick::IsButtonDown(jstick::XBoxButton::BumperR))
|
||||||
|
std::cout << "Right Bumper Pressed" << std::endl;
|
||||||
|
|
||||||
|
if (jstick::IsButtonDown(jstick::XBoxButton::ThumbL))
|
||||||
|
std::cout << "Left Thumb Pressed" << std::endl;
|
||||||
|
|
||||||
|
if (jstick::IsButtonDown(jstick::XBoxButton::ThumbR))
|
||||||
|
std::cout << "Right Thumb Pressed" << std::endl;
|
||||||
|
|
||||||
|
if (!jstick::GetDPadIsAxisOrButtons())
|
||||||
|
{
|
||||||
|
if (jstick::IsButtonDown(jstick::XBoxButton::DPadUp))
|
||||||
|
std::cout << "DPad Up Pressed" << std::endl;
|
||||||
|
|
||||||
|
if (jstick::IsButtonDown(jstick::XBoxButton::DPadDown))
|
||||||
|
std::cout << "DPad Down Pressed" << std::endl;
|
||||||
|
|
||||||
|
if (jstick::IsButtonDown(jstick::XBoxButton::DPadLeft))
|
||||||
|
std::cout << "DPad Left Pressed" << std::endl;
|
||||||
|
|
||||||
|
if (jstick::IsButtonDown(jstick::XBoxButton::DPadRight))
|
||||||
|
std::cout << "DPad Right Pressed" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(10ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
@@ -1,256 +0,0 @@
|
|||||||
#include <jstick.hpp>
|
|
||||||
|
|
||||||
#ifdef UNIX
|
|
||||||
#include <linux/joystick.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <bits/fs_fwd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
//#include <joystickapi.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <format>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <J3ML/LinearAlgebra/Vector2i.hpp>
|
|
||||||
|
|
||||||
struct axis_state {
|
|
||||||
short x, y;
|
|
||||||
};
|
|
||||||
|
|
||||||
int js_handle;
|
|
||||||
bool js_connected;
|
|
||||||
struct js_event event;
|
|
||||||
struct axis_state axes[3] = {0};
|
|
||||||
bool btn_state[10] = {false};
|
|
||||||
size_t axis;
|
|
||||||
Vector2 l_thumb;
|
|
||||||
Vector2 r_thumb;
|
|
||||||
short l_trigger;
|
|
||||||
short r_trigger;
|
|
||||||
Vector2 dpad;
|
|
||||||
jstick::ControllerType device_type;
|
|
||||||
|
|
||||||
|
|
||||||
/// Reads a joystick event from the joystick device.
|
|
||||||
/// @returns 0 on success. Otherwise -1 is returned.
|
|
||||||
int read_event(int fd, struct js_event *event) {
|
|
||||||
ssize_t bytes;
|
|
||||||
|
|
||||||
bytes = read(fd, event, sizeof(*event));
|
|
||||||
|
|
||||||
if (bytes == sizeof(*event))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/// Error, could not read full event.
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string js_device_file(int hwid) {
|
|
||||||
return std::format("/dev/input/js{}", hwid);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool jstick::JoystickDetected(int jsHandle) {
|
|
||||||
return std::filesystem::exists(js_device_file(jsHandle));
|
|
||||||
}
|
|
||||||
|
|
||||||
int jstick::NumJoysticksDetected() {
|
|
||||||
int max = 4;
|
|
||||||
for (int i = 0; i < max; i++) {
|
|
||||||
if (!JoystickDetected(i))
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
return max;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool jstick::Connected(int hwid) {
|
|
||||||
return js_connected && js_handle == hwid;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string jstick::GetDeviceName(int hwid) {
|
|
||||||
char name_buf[128];
|
|
||||||
if (ioctl(hwid, JSIOCGNAME(sizeof(name_buf)), name_buf) < 0)
|
|
||||||
strncpy(name_buf, "Unknown", sizeof(name_buf));
|
|
||||||
return std::string(name_buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
int jstick::Connect(int hwid) {
|
|
||||||
if (js_connected) {
|
|
||||||
std::cerr << "Joystick already connected! Support for multiple joysticks is in-progress!" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
js_handle = open(js_device_file(hwid).c_str(), O_NONBLOCK);
|
|
||||||
|
|
||||||
if (js_handle == -1) {
|
|
||||||
perror("Could not open joystick");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
js_connected = true;
|
|
||||||
device_type = GetDeviceTypeFromName(GetDeviceName(js_handle));
|
|
||||||
JoystickConnected.Invoke(js_handle);
|
|
||||||
return js_handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool jstick::Disconnect(int hwid) {
|
|
||||||
if (!js_connected || js_handle == -1) {
|
|
||||||
std::cerr << "Joystick is already disconnected! Support for multiple joysticks is in-progrss!" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
close(js_handle);
|
|
||||||
JoystickDisconnected.Invoke(js_handle);
|
|
||||||
js_connected = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void jstick::JoystickServiceUpdate() {
|
|
||||||
|
|
||||||
int device_count = NumJoysticksDetected();
|
|
||||||
|
|
||||||
bool has = JoystickDetected();
|
|
||||||
|
|
||||||
if (has && !js_connected)
|
|
||||||
Connect();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void ProcessButtonEvent(uint8_t button_index, bool value) {
|
|
||||||
|
|
||||||
jstick::XBoxButton btn = (jstick::XBoxButton)button_index;
|
|
||||||
|
|
||||||
btn_state[button_index] = value;
|
|
||||||
|
|
||||||
if (value) {
|
|
||||||
jstick::ButtonPressed.Invoke(btn);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
jstick::ButtonReleased.Invoke(btn);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessAxisEvent(uint8_t axis, short value) {
|
|
||||||
// TODO: Handle all single-axis changes, and then invoke the axis event with the new vectors.
|
|
||||||
|
|
||||||
Vector2 prev_lthumb = l_thumb;
|
|
||||||
Vector2 prev_rthumb = r_thumb;
|
|
||||||
Vector2 prev_dp = dpad;
|
|
||||||
short prev_ltrigger = l_trigger;
|
|
||||||
short prev_rtrigger = r_trigger;
|
|
||||||
|
|
||||||
if (device_type == jstick::ControllerType::XBox)
|
|
||||||
{
|
|
||||||
if (axis == 0) l_thumb.x = value;
|
|
||||||
if (axis == 1) l_thumb.y = value;
|
|
||||||
if (axis == 2) l_trigger = value;
|
|
||||||
if (axis == 3) r_thumb.x = value;
|
|
||||||
if (axis == 4) r_thumb.y = value;
|
|
||||||
if (axis == 5) r_trigger = value;
|
|
||||||
if (axis == 6) dpad.x = value;
|
|
||||||
if (axis == 7) dpad.y = value;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (device_type == jstick::ControllerType::PS3) {
|
|
||||||
if (axis == 0) l_thumb.x = value;
|
|
||||||
if (axis == 1) l_thumb.y = value;
|
|
||||||
if (axis == 2) r_thumb.x = value;
|
|
||||||
if (axis == 3) r_thumb.y = value;
|
|
||||||
if (axis == 4) l_trigger = value;
|
|
||||||
if (axis == 5) r_trigger = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (device_type == jstick::ControllerType::PS4) {
|
|
||||||
if (axis == 0) l_thumb.x = value;
|
|
||||||
if (axis == 1) l_thumb.y = value;
|
|
||||||
if (axis == 2) r_thumb.x = value;
|
|
||||||
if (axis == 3) r_thumb.y = value;
|
|
||||||
if (axis == 4) l_trigger = value;
|
|
||||||
if (axis == 5) r_trigger = value;
|
|
||||||
if (axis == 6) dpad.x = value;
|
|
||||||
if (axis == 7) dpad.y = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Compare against the l_thumb from the last time the event was called.
|
|
||||||
if (l_thumb.DistanceSq(prev_lthumb) > 0.1f)
|
|
||||||
jstick::LeftThumbstickMoved.Invoke(l_thumb);
|
|
||||||
if (r_thumb.DistanceSq(prev_rthumb) > 0.1f)
|
|
||||||
jstick::RightThumbstickMoved.Invoke(r_thumb);
|
|
||||||
if (dpad.DistanceSq(prev_dp) > 0.1f)
|
|
||||||
jstick::DPadMoved.Invoke(dpad);
|
|
||||||
|
|
||||||
if (Math::Abs(l_trigger) - Math::Abs(prev_ltrigger) > 1.f)
|
|
||||||
jstick::LeftTriggerMoved.Invoke(l_trigger);
|
|
||||||
|
|
||||||
if (Math::Abs(r_trigger) - Math::Abs(prev_rtrigger) > 1.f)
|
|
||||||
jstick::RightTriggerMoved.Invoke(r_trigger);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void jstick::ReadEventLoop() {
|
|
||||||
while (read_event(js_handle, &event) == 0) {
|
|
||||||
switch (event.type) {
|
|
||||||
case JS_EVENT_BUTTON:
|
|
||||||
ProcessButtonEvent(event.number, event.value != 0);
|
|
||||||
break;
|
|
||||||
case JS_EVENT_AXIS:
|
|
||||||
ProcessAxisEvent(event.number, event.value);
|
|
||||||
break;
|
|
||||||
case JS_EVENT_INIT:
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool jstick::IsButtonDown(const XBoxButton &btn) {
|
|
||||||
return btn_state[(uint8_t)btn];
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector2 jstick::GetLeftThumbstickAxis(float deadzone) { return l_thumb;}
|
|
||||||
Vector2 jstick::GetRightThumbstickAxis(float deadzone) { return r_thumb; }
|
|
||||||
Vector2 jstick::GetDPadAxis(float deadzone) { return dpad; }
|
|
||||||
|
|
||||||
|
|
||||||
short jstick::GetRightTrigger() { return r_trigger;}
|
|
||||||
short jstick::GetLeftTrigger() { return l_trigger; }
|
|
||||||
|
|
||||||
constexpr float short_range = 32768.f;
|
|
||||||
|
|
||||||
Vector2 jstick::GetLeftThumbstickAxisNormalized(float deadzone) {
|
|
||||||
return l_thumb / short_range;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector2 jstick::GetRightThumbstickAxisNormalized(float deadzone) {
|
|
||||||
return r_thumb / short_range;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector2 jstick::GetDPadAxisNormalized(float deadzone) {
|
|
||||||
return dpad / short_range;
|
|
||||||
}
|
|
||||||
|
|
||||||
float jstick::GetLeftTriggerNormalized() {
|
|
||||||
return l_trigger / short_range;
|
|
||||||
}
|
|
||||||
|
|
||||||
float jstick::GetRightTriggerNormalized() {
|
|
||||||
return r_trigger / short_range;
|
|
||||||
}
|
|
||||||
|
|
||||||
jstick::ControllerType jstick::GetDeviceTypeFromName(const std::string &name) {
|
|
||||||
if (name == "Microsoft X-Box 360 pad")
|
|
||||||
return ControllerType::XBox;
|
|
||||||
|
|
||||||
|
|
||||||
return ControllerType::Unknown;
|
|
||||||
}
|
|
@@ -1,3 +1,258 @@
|
|||||||
//
|
#include <jstick.hpp>
|
||||||
// Created by josh on 5/22/2025.
|
|
||||||
//
|
#ifdef UNIX
|
||||||
|
#include <linux/joystick.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <bits/fs_fwd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
//#include <joystickapi.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <format>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <J3ML/LinearAlgebra/Vector2i.hpp>
|
||||||
|
|
||||||
|
struct axis_state {
|
||||||
|
short x, y;
|
||||||
|
};
|
||||||
|
|
||||||
|
int js_handle;
|
||||||
|
bool js_connected;
|
||||||
|
struct js_event event;
|
||||||
|
struct axis_state axes[3] = {0};
|
||||||
|
bool btn_state[10] = {false};
|
||||||
|
size_t axis;
|
||||||
|
Vector2 l_thumb;
|
||||||
|
Vector2 r_thumb;
|
||||||
|
short l_trigger;
|
||||||
|
short r_trigger;
|
||||||
|
Vector2 dpad;
|
||||||
|
jstick::ControllerType device_type;
|
||||||
|
|
||||||
|
|
||||||
|
/// Reads a joystick event from the joystick device.
|
||||||
|
/// @returns 0 on success. Otherwise -1 is returned.
|
||||||
|
int read_event(int fd, struct js_event *event) {
|
||||||
|
ssize_t bytes;
|
||||||
|
|
||||||
|
bytes = read(fd, event, sizeof(*event));
|
||||||
|
|
||||||
|
if (bytes == sizeof(*event))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/// Error, could not read full event.
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string js_device_file(int hwid) {
|
||||||
|
return std::format("/dev/input/js{}", hwid);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool jstick::JoystickDetected(int jsHandle) {
|
||||||
|
return std::filesystem::exists(js_device_file(jsHandle));
|
||||||
|
}
|
||||||
|
|
||||||
|
int jstick::NumJoysticksDetected() {
|
||||||
|
int max = 4;
|
||||||
|
for (int i = 0; i < max; i++) {
|
||||||
|
if (!JoystickDetected(i))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool jstick::Connected(int hwid) {
|
||||||
|
return js_connected && js_handle == hwid;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string jstick::GetDeviceName(int hwid) {
|
||||||
|
char name_buf[128];
|
||||||
|
if (ioctl(hwid, JSIOCGNAME(sizeof(name_buf)), name_buf) < 0)
|
||||||
|
strncpy(name_buf, "Unknown", sizeof(name_buf));
|
||||||
|
return std::string(name_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool jstick::GetDPadIsAxisOrButtons() { return true; }
|
||||||
|
|
||||||
|
int jstick::Connect(int hwid) {
|
||||||
|
if (js_connected) {
|
||||||
|
std::cerr << "Joystick already connected! Support for multiple joysticks is in-progress!" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
js_handle = open(js_device_file(hwid).c_str(), O_NONBLOCK);
|
||||||
|
|
||||||
|
if (js_handle == -1) {
|
||||||
|
perror("Could not open joystick");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
js_connected = true;
|
||||||
|
device_type = GetDeviceTypeFromName(GetDeviceName(js_handle));
|
||||||
|
JoystickConnected.Invoke(js_handle);
|
||||||
|
return js_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool jstick::Disconnect(int hwid) {
|
||||||
|
if (!js_connected || js_handle == -1) {
|
||||||
|
std::cerr << "Joystick is already disconnected! Support for multiple joysticks is in-progrss!" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
close(js_handle);
|
||||||
|
JoystickDisconnected.Invoke(js_handle);
|
||||||
|
js_connected = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jstick::JoystickServiceUpdate() {
|
||||||
|
|
||||||
|
int device_count = NumJoysticksDetected();
|
||||||
|
|
||||||
|
bool has = JoystickDetected();
|
||||||
|
|
||||||
|
if (has && !js_connected)
|
||||||
|
Connect();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void ProcessButtonEvent(uint8_t button_index, bool value) {
|
||||||
|
|
||||||
|
jstick::XBoxButton btn = (jstick::XBoxButton)button_index;
|
||||||
|
|
||||||
|
btn_state[button_index] = value;
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
jstick::ButtonPressed.Invoke(btn);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
jstick::ButtonReleased.Invoke(btn);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessAxisEvent(uint8_t axis, short value) {
|
||||||
|
// TODO: Handle all single-axis changes, and then invoke the axis event with the new vectors.
|
||||||
|
|
||||||
|
Vector2 prev_lthumb = l_thumb;
|
||||||
|
Vector2 prev_rthumb = r_thumb;
|
||||||
|
Vector2 prev_dp = dpad;
|
||||||
|
short prev_ltrigger = l_trigger;
|
||||||
|
short prev_rtrigger = r_trigger;
|
||||||
|
|
||||||
|
if (device_type == jstick::ControllerType::XBox)
|
||||||
|
{
|
||||||
|
if (axis == 0) l_thumb.x = value;
|
||||||
|
if (axis == 1) l_thumb.y = value;
|
||||||
|
if (axis == 2) l_trigger = value;
|
||||||
|
if (axis == 3) r_thumb.x = value;
|
||||||
|
if (axis == 4) r_thumb.y = value;
|
||||||
|
if (axis == 5) r_trigger = value;
|
||||||
|
if (axis == 6) dpad.x = value;
|
||||||
|
if (axis == 7) dpad.y = value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device_type == jstick::ControllerType::PS3) {
|
||||||
|
if (axis == 0) l_thumb.x = value;
|
||||||
|
if (axis == 1) l_thumb.y = value;
|
||||||
|
if (axis == 2) r_thumb.x = value;
|
||||||
|
if (axis == 3) r_thumb.y = value;
|
||||||
|
if (axis == 4) l_trigger = value;
|
||||||
|
if (axis == 5) r_trigger = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device_type == jstick::ControllerType::PS4) {
|
||||||
|
if (axis == 0) l_thumb.x = value;
|
||||||
|
if (axis == 1) l_thumb.y = value;
|
||||||
|
if (axis == 2) r_thumb.x = value;
|
||||||
|
if (axis == 3) r_thumb.y = value;
|
||||||
|
if (axis == 4) l_trigger = value;
|
||||||
|
if (axis == 5) r_trigger = value;
|
||||||
|
if (axis == 6) dpad.x = value;
|
||||||
|
if (axis == 7) dpad.y = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Compare against the l_thumb from the last time the event was called.
|
||||||
|
if (l_thumb.DistanceSq(prev_lthumb) > 0.1f)
|
||||||
|
jstick::LeftThumbstickMoved.Invoke(l_thumb);
|
||||||
|
if (r_thumb.DistanceSq(prev_rthumb) > 0.1f)
|
||||||
|
jstick::RightThumbstickMoved.Invoke(r_thumb);
|
||||||
|
if (dpad.DistanceSq(prev_dp) > 0.1f)
|
||||||
|
jstick::DPadMoved.Invoke(dpad);
|
||||||
|
|
||||||
|
if (Math::Abs(l_trigger) - Math::Abs(prev_ltrigger) > 1.f)
|
||||||
|
jstick::LeftTriggerMoved.Invoke(l_trigger);
|
||||||
|
|
||||||
|
if (Math::Abs(r_trigger) - Math::Abs(prev_rtrigger) > 1.f)
|
||||||
|
jstick::RightTriggerMoved.Invoke(r_trigger);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void jstick::ReadEventLoop() {
|
||||||
|
while (read_event(js_handle, &event) == 0) {
|
||||||
|
switch (event.type) {
|
||||||
|
case JS_EVENT_BUTTON:
|
||||||
|
ProcessButtonEvent(event.number, event.value != 0);
|
||||||
|
break;
|
||||||
|
case JS_EVENT_AXIS:
|
||||||
|
ProcessAxisEvent(event.number, event.value);
|
||||||
|
break;
|
||||||
|
case JS_EVENT_INIT:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool jstick::IsButtonDown(const XBoxButton &btn) {
|
||||||
|
return btn_state[(uint8_t)btn];
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2 jstick::GetLeftThumbstickAxis(float deadzone) { return l_thumb;}
|
||||||
|
Vector2 jstick::GetRightThumbstickAxis(float deadzone) { return r_thumb; }
|
||||||
|
Vector2 jstick::GetDPadAxis(float deadzone) { return dpad; }
|
||||||
|
|
||||||
|
|
||||||
|
short jstick::GetRightTrigger() { return r_trigger;}
|
||||||
|
short jstick::GetLeftTrigger() { return l_trigger; }
|
||||||
|
|
||||||
|
constexpr float short_range = 32768.f;
|
||||||
|
|
||||||
|
Vector2 jstick::GetLeftThumbstickAxisNormalized(float deadzone) {
|
||||||
|
return l_thumb / short_range;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2 jstick::GetRightThumbstickAxisNormalized(float deadzone) {
|
||||||
|
return r_thumb / short_range;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2 jstick::GetDPadAxisNormalized(float deadzone) {
|
||||||
|
return dpad / short_range;
|
||||||
|
}
|
||||||
|
|
||||||
|
float jstick::GetLeftTriggerNormalized() {
|
||||||
|
return l_trigger / short_range;
|
||||||
|
}
|
||||||
|
|
||||||
|
float jstick::GetRightTriggerNormalized() {
|
||||||
|
return r_trigger / short_range;
|
||||||
|
}
|
||||||
|
|
||||||
|
jstick::ControllerType jstick::GetDeviceTypeFromName(const std::string &name) {
|
||||||
|
if (name == "Microsoft X-Box 360 pad")
|
||||||
|
return ControllerType::XBox;
|
||||||
|
|
||||||
|
|
||||||
|
return ControllerType::Unknown;
|
||||||
|
}
|
||||||
|
@@ -3,15 +3,30 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <XInput.h>
|
#include <XInput.h>
|
||||||
|
|
||||||
|
#pragma comment(lib, "XInput.lib")
|
||||||
|
|
||||||
|
#define MAX_DEVICES 4
|
||||||
|
|
||||||
|
bool device_exists[MAX_DEVICES];
|
||||||
|
XINPUT_STATE cur_state[MAX_DEVICES];
|
||||||
|
XINPUT_STATE prev_state[MAX_DEVICES];
|
||||||
|
|
||||||
bool jstick::Initialize()
|
bool jstick::Initialize()
|
||||||
{
|
{
|
||||||
|
// TODO: Anything.
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
/// XInput treats the DPad as buttons.
|
||||||
|
bool jstick::GetDPadIsAxisOrButtons() { return false; }
|
||||||
|
|
||||||
|
/// XInput does not work via an event-poll loop, so we do nothing here.
|
||||||
bool jstick::JoystickDetected(int jsHandle)
|
void jstick::ReadEventLoop()
|
||||||
{
|
{
|
||||||
DWORD dwUserIndex = (DWORD)jsHandle;
|
prev_state[0] = cur_state[0];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
DWORD dwUserIndex = (DWORD)0;
|
||||||
|
|
||||||
XINPUT_STATE state;
|
XINPUT_STATE state;
|
||||||
ZeroMemory(&state, sizeof(XINPUT_STATE));
|
ZeroMemory(&state, sizeof(XINPUT_STATE));
|
||||||
@@ -20,6 +35,33 @@ bool jstick::JoystickDetected(int jsHandle)
|
|||||||
|
|
||||||
if (result == ERROR_SUCCESS)
|
if (result == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
|
cur_state[0] = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jstick::Vibrate(int LV, int RV, int hwid)
|
||||||
|
{
|
||||||
|
|
||||||
|
XINPUT_VIBRATION vibration;
|
||||||
|
ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION));
|
||||||
|
|
||||||
|
vibration.wLeftMotorSpeed = LV;
|
||||||
|
vibration.wRightMotorSpeed = RV;
|
||||||
|
|
||||||
|
XInputSetState(hwid, &vibration);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool jstick::JoystickDetected(int hwid)
|
||||||
|
{
|
||||||
|
DWORD dwUserIndex = (DWORD)hwid;
|
||||||
|
|
||||||
|
XINPUT_STATE state;
|
||||||
|
ZeroMemory(&state, sizeof(XINPUT_STATE));
|
||||||
|
|
||||||
|
DWORD result = XInputGetState(dwUserIndex, &state);
|
||||||
|
|
||||||
|
if (result == ERROR_SUCCESS) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
std::cout << "Error Code: " << result << std::endl;
|
std::cout << "Error Code: " << result << std::endl;
|
||||||
@@ -27,3 +69,37 @@ bool jstick::JoystickDetected(int jsHandle)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool jstick::IsButtonDown(const XBoxButton& btn, int hwid)
|
||||||
|
{
|
||||||
|
XINPUT_STATE state;
|
||||||
|
ZeroMemory(&state, sizeof(XINPUT_STATE));
|
||||||
|
|
||||||
|
DWORD result = XInputGetState(hwid, &state);
|
||||||
|
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (btn) {
|
||||||
|
case XBoxButton::Back: if (state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) { return true; } break;
|
||||||
|
case XBoxButton::Start: if (state.Gamepad.wButtons & XINPUT_GAMEPAD_START) { return true; } break;
|
||||||
|
case XBoxButton::ThumbL: if (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) { return true; } break;
|
||||||
|
case XBoxButton::ThumbR: if (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) { return true; } break;
|
||||||
|
case XBoxButton::ButtonA: if (state.Gamepad.wButtons & XINPUT_GAMEPAD_A) { return true; } break;
|
||||||
|
case XBoxButton::ButtonB: if (state.Gamepad.wButtons & XINPUT_GAMEPAD_B) { return true; } break;
|
||||||
|
case XBoxButton::ButtonX: if (state.Gamepad.wButtons & XINPUT_GAMEPAD_X) { return true; } break;
|
||||||
|
case XBoxButton::ButtonY: if (state.Gamepad.wButtons & XINPUT_GAMEPAD_Y) { return true; } break;
|
||||||
|
|
||||||
|
case XBoxButton::BumperL: if (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) { return true; } break;
|
||||||
|
case XBoxButton::BumperR: if (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) { return true; } break;
|
||||||
|
|
||||||
|
case XBoxButton::DPadUp: if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) { return true; } break;
|
||||||
|
case XBoxButton::DPadLeft: if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) { return true; } break;
|
||||||
|
case XBoxButton::DPadDown: if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) { return true; } break;
|
||||||
|
case XBoxButton::DPadRight: if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) { return true; } break;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
1
src/shared/jstick_shared.cpp
Normal file
1
src/shared/jstick_shared.cpp
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#pragma bruh
|
Reference in New Issue
Block a user