Code refactor
This commit is contained in:
parent
89f348f7c1
commit
d6e6162db5
21 changed files with 1342 additions and 1251 deletions
|
|
@ -15,7 +15,7 @@ set(THREADS_PREFER_PTHREAD_FLAG ON)
|
|||
find_package(Threads REQUIRED)
|
||||
|
||||
# Adding something we can run - Output name matches target name
|
||||
add_executable(${EXECUTABLE_NAME} dev_iio.c input_dev.c logic.c main.c output_dev.c platform.c queue.c settings.c virt_ds4.c virt_ds5.c)
|
||||
add_executable(${EXECUTABLE_NAME} dev_iio.c input_dev.c logic.c main.c output_dev.c platform.c queue.c settings.c virt_ds4.c virt_ds5.c virt_mouse_kbd.c virt_evdev.c devices_status.c)
|
||||
|
||||
target_link_libraries(${EXECUTABLE_NAME} PRIVATE Threads::Threads -levdev -ludev -lconfig)
|
||||
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -2,7 +2,7 @@
|
|||
CFLAGS= -O3 -march=znver4 -D _DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112L -std=c11 -fPIE -pedantic -Wall -flto=full # -Werror
|
||||
LDFLAGS=-lpthread -levdev -ludev -lconfig -lrt -lm -flto=full
|
||||
CC=clang
|
||||
OBJECTS=main.o input_dev.o dev_iio.o output_dev.o queue.o logic.o platform.o settings.o virt_ds4.o virt_ds5.o
|
||||
OBJECTS=main.o input_dev.o dev_iio.o output_dev.o queue.o logic.o platform.o settings.o virt_ds4.o virt_ds5.o virt_mouse_kbd.o virt_evdev.o devices_status.o
|
||||
TARGET=rogue-enemy
|
||||
|
||||
all: $(TARGET)
|
||||
|
|
|
|||
5
TODO.md
Normal file
5
TODO.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# TODO
|
||||
List of great ideas that are such only as soon as I don't implement them:
|
||||
|
||||
- In DualShock one can simulate touchpad press with back buttons
|
||||
- DualShock and DualSense do send HID reports at 800Hz, so the iio sampling rate should be at least 1600Hz
|
||||
45
devices_status.c
Normal file
45
devices_status.c
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#include "devices_status.h"
|
||||
|
||||
void kbd_status_init(keyboard_status_t *const stats) {
|
||||
stats->connected = true;
|
||||
}
|
||||
|
||||
void gamepad_status_init(gamepad_status_t *const stats) {
|
||||
stats->connected = true;
|
||||
stats->joystick_positions[0][0] = 0;
|
||||
stats->joystick_positions[0][1] = 0;
|
||||
stats->joystick_positions[1][0] = 0;
|
||||
stats->joystick_positions[1][1] = 0;
|
||||
stats->dpad = 0x00;
|
||||
stats->l2_trigger = 0;
|
||||
stats->r2_trigger = 0;
|
||||
stats->triangle = 0;
|
||||
stats->circle = 0;
|
||||
stats->cross = 0;
|
||||
stats->square = 0;
|
||||
stats->r3 = 0;
|
||||
stats->r3 = 0;
|
||||
stats->option = 0;
|
||||
stats->share = 0;
|
||||
stats->center = 0;
|
||||
stats->r4 = 0;
|
||||
stats->l4 = 0;
|
||||
stats->r5 = 0;
|
||||
stats->l5 = 0;
|
||||
stats->motors_intensity[0] = 0;
|
||||
stats->motors_intensity[1] = 0;
|
||||
stats->rumble_events_count = 0;
|
||||
stats->gyro[0] = 0;
|
||||
stats->gyro[1] = 0;
|
||||
stats->gyro[2] = 0;
|
||||
stats->accel[0] = 0;
|
||||
stats->accel[1] = 0;
|
||||
stats->accel[2] = 0;
|
||||
stats->flags = 0;
|
||||
}
|
||||
|
||||
void devices_status_init(devices_status_t *const stats) {
|
||||
gamepad_status_init(&stats->gamepad);
|
||||
kbd_status_init(&stats->kbd);
|
||||
// TODO: mouse init
|
||||
}
|
||||
73
devices_status.h
Normal file
73
devices_status.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
typedef struct gamepad_status {
|
||||
bool connected;
|
||||
|
||||
int32_t joystick_positions[2][2]; // [0 left | 1 right][x axis | y axis]
|
||||
|
||||
uint8_t dpad; // 0x00 x - | 0x01 x -> | 0x02 x <- | 0x00 y - | 0x10 y ^ | 0x10 y . |
|
||||
|
||||
uint8_t l2_trigger;
|
||||
uint8_t r2_trigger;
|
||||
|
||||
uint8_t triangle;
|
||||
uint8_t circle;
|
||||
uint8_t cross;
|
||||
uint8_t square;
|
||||
|
||||
uint8_t l1;
|
||||
uint8_t r1;
|
||||
|
||||
uint8_t r3;
|
||||
uint8_t l3;
|
||||
|
||||
uint8_t option;
|
||||
uint8_t share;
|
||||
uint8_t center;
|
||||
|
||||
uint8_t l4;
|
||||
uint8_t r4;
|
||||
|
||||
uint8_t l5;
|
||||
uint8_t r5;
|
||||
|
||||
struct timeval last_gyro_motion_time;
|
||||
struct timeval last_accel_motion_time;
|
||||
|
||||
double gyro[3]; // | x, y, z| right-hand-rules -- in rad/s
|
||||
double accel[3]; // | x, y, z| positive: right, up, towards player -- in m/s^2
|
||||
|
||||
int16_t raw_gyro[3];
|
||||
int16_t raw_accel[3];
|
||||
|
||||
uint64_t rumble_events_count;
|
||||
uint8_t motors_intensity[2]; // 0 = left, 1 = right
|
||||
|
||||
volatile uint32_t flags;
|
||||
|
||||
} gamepad_status_t;
|
||||
|
||||
typedef struct keyboard_status {
|
||||
bool connected;
|
||||
} keyboard_status_t;
|
||||
|
||||
typedef struct devices_status {
|
||||
// this mutex MUST be grabbed when reading and/or writing below properties
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
gamepad_status_t gamepad;
|
||||
|
||||
keyboard_status_t kbd;
|
||||
|
||||
} devices_status_t;
|
||||
|
||||
void kbd_status_init(keyboard_status_t *const stats);
|
||||
|
||||
void gamepad_status_init(gamepad_status_t *const stats);
|
||||
|
||||
void devices_status_init(devices_status_t *const stats);
|
||||
140
logic.c
140
logic.c
|
|
@ -3,67 +3,44 @@
|
|||
#include "queue.h"
|
||||
#include "virt_ds4.h"
|
||||
#include "virt_ds5.h"
|
||||
#include "virt_evdev.h"
|
||||
#include "virt_mouse_kbd.h"
|
||||
|
||||
static const char* configuration_file = "/etc/ROGueENEMY/config.cfg";
|
||||
|
||||
int logic_create(logic_t *const logic) {
|
||||
int ret = 0;
|
||||
|
||||
logic->flags = 0x00000000U;
|
||||
|
||||
memset(logic->gamepad.joystick_positions, 0, sizeof(logic->gamepad.joystick_positions));
|
||||
logic->gamepad.dpad = 0x00;
|
||||
logic->gamepad.l2_trigger = 0;
|
||||
logic->gamepad.r2_trigger = 0;
|
||||
logic->gamepad.triangle = 0;
|
||||
logic->gamepad.circle = 0;
|
||||
logic->gamepad.cross = 0;
|
||||
logic->gamepad.square = 0;
|
||||
logic->gamepad.r3 = 0;
|
||||
logic->gamepad.r3 = 0;
|
||||
logic->gamepad.option = 0;
|
||||
logic->gamepad.share = 0;
|
||||
logic->gamepad.center = 0;
|
||||
logic->gamepad.r4 = 0;
|
||||
logic->gamepad.l4 = 0;
|
||||
logic->gamepad.r5 = 0;
|
||||
logic->gamepad.l5 = 0;
|
||||
logic->gamepad.rumble_events_count = 0;
|
||||
memset(logic->gamepad.gyro, 0, sizeof(logic->gamepad.gyro));
|
||||
memset(logic->gamepad.accel, 0, sizeof(logic->gamepad.accel));
|
||||
logic->gamepad.flags = 0;
|
||||
init_config(&logic->controller_settings);
|
||||
const int fill_config_res = fill_config(&logic->controller_settings, configuration_file);
|
||||
if (fill_config_res != 0) {
|
||||
fprintf(stderr, "Unable to fill configuration from file %s -- defaults will be used\n", configuration_file);
|
||||
}
|
||||
|
||||
const int mutex_creation_res = pthread_mutex_init(&logic->gamepad_mutex, NULL);
|
||||
if (mutex_creation_res != 0) {
|
||||
fprintf(stderr, "Unable to create mutex: %d\n", mutex_creation_res);
|
||||
return mutex_creation_res;
|
||||
devices_status_init(&logic->dev_stats);
|
||||
|
||||
ret = queue_init(&logic->rumble_events_queue, 1);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Unable to create the rumble events queue: %d\n", ret);
|
||||
goto logic_create_err;
|
||||
}
|
||||
|
||||
ret = pthread_mutex_init(&logic->dev_stats.mutex, NULL);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Unable to create mutex: %d\n", ret);
|
||||
goto logic_create_err;
|
||||
}
|
||||
|
||||
const int queue_init_res = queue_init(&logic->input_queue, 128);
|
||||
|
||||
const int virt_ds4_thread_creation = pthread_create(&logic->virt_ds4_thread, NULL, virt_ds4_thread_func, (void*)(logic));
|
||||
if (virt_ds4_thread_creation != 0) {
|
||||
fprintf(stderr, "Error creating virtual DualShock4 thread: %d. Will use evdev as output.\n", virt_ds4_thread_creation);
|
||||
|
||||
logic->gamepad_output = GAMEPAD_OUTPUT_EVDEV;
|
||||
} else {
|
||||
printf("Creation of virtual DualShock4 succeeded: using it as the defaut output.\n");
|
||||
logic->flags |= LOGIC_FLAGS_VIRT_DS4_ENABLE;
|
||||
logic->gamepad_output = GAMEPAD_OUTPUT_DS4;
|
||||
}
|
||||
|
||||
const int virt_ds5_thread_creation = pthread_create(&logic->virt_ds5_thread, NULL, virt_ds5_thread_func, (void*)(logic));
|
||||
if (virt_ds4_thread_creation != 0) {
|
||||
fprintf(stderr, "Error creating virtual DualSense thread: %d.\n", virt_ds5_thread_creation);
|
||||
} else {
|
||||
printf("Creation of virtual DualShock4 succeeded: using it as the defaut output.\n");
|
||||
logic->flags |= LOGIC_FLAGS_VIRT_DS5_ENABLE;
|
||||
logic->gamepad_output = GAMEPAD_OUTPUT_DS5;
|
||||
}
|
||||
|
||||
if (queue_init_res < 0) {
|
||||
fprintf(stderr, "Unable to create queue: %d\n", queue_init_res);
|
||||
return queue_init_res;
|
||||
}
|
||||
|
||||
bool lizard_thread_started = false;
|
||||
const int init_platform_res = init_platform(&logic->platform);
|
||||
if (init_platform_res == 0) {
|
||||
printf("RC71L platform correctly initialized\n");
|
||||
|
|
@ -71,35 +48,74 @@ int logic_create(logic_t *const logic) {
|
|||
logic->flags |= LOGIC_FLAGS_PLATFORM_ENABLE;
|
||||
|
||||
if (is_mouse_mode(&logic->platform)) {
|
||||
printf("Gamepad output will default to evdev when the controller is set in mouse mode.\n");
|
||||
logic->gamepad_output = GAMEPAD_OUTPUT_EVDEV;
|
||||
} else if (is_gamepad_mode(&logic->platform)) {
|
||||
logic->gamepad_output = (logic->flags & LOGIC_FLAGS_VIRT_DS5_ENABLE) ? GAMEPAD_OUTPUT_DS5 : ((logic->flags & LOGIC_FLAGS_VIRT_DS4_ENABLE) ? GAMEPAD_OUTPUT_DS4: GAMEPAD_OUTPUT_EVDEV);
|
||||
} else if (is_macro_mode(&logic->platform)) {
|
||||
logic->gamepad_output = (logic->flags & LOGIC_FLAGS_VIRT_DS4_ENABLE) ? GAMEPAD_OUTPUT_DS4 : GAMEPAD_OUTPUT_EVDEV;
|
||||
}
|
||||
printf("Device is in lizard mode\n");
|
||||
// TODO: start the appropriate output thread
|
||||
|
||||
printf("Gamepad output is %d\n", (int)logic->gamepad_output);
|
||||
lizard_thread_started = true;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Unable to initialize Asus RC71L MCU: %d\n", init_platform_res);
|
||||
logic->gamepad_output = (logic->flags & LOGIC_FLAGS_VIRT_DS4_ENABLE) ? GAMEPAD_OUTPUT_DS4 : GAMEPAD_OUTPUT_EVDEV;
|
||||
}
|
||||
|
||||
queue_init(&logic->rumble_events_queue, 1);
|
||||
|
||||
init_config(&logic->controller_settings);
|
||||
const int fill_config_res = fill_config(&logic->controller_settings, configuration_file);
|
||||
if (fill_config_res != 0) {
|
||||
fprintf(stderr, "Unable to fill configuration from file %s\n", configuration_file);
|
||||
if (!lizard_thread_started) {
|
||||
logic_start_output_dev_thread(logic);
|
||||
}
|
||||
|
||||
return 0;
|
||||
logic_create_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int is_rc71l_ready(const logic_t *const logic) {
|
||||
return logic->flags & LOGIC_FLAGS_PLATFORM_ENABLE;
|
||||
}
|
||||
|
||||
void logic_terminate_output_thread(logic_t *const logic) {
|
||||
if (logic->virt_dev_thread_running) {
|
||||
void* thread_return = NULL;
|
||||
pthread_join(logic->virt_dev_thread, &thread_return);
|
||||
}
|
||||
}
|
||||
|
||||
int logic_start_output_mouse_kbd_thread(logic_t *const logic) {
|
||||
// TODO: logic->dev_stats.mouse_kbd.connected = true;
|
||||
const int ret = pthread_create(&logic->virt_dev_thread, NULL, virt_mouse_kbd_thread_func, (void*)(&logic->dev_stats));
|
||||
|
||||
logic->virt_dev_thread_running = ret == 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int logic_start_output_dev_thread(logic_t *const logic) {
|
||||
logic->dev_stats.gamepad.connected = true;
|
||||
|
||||
int ret = -EINVAL;
|
||||
switch (logic->controller_settings.gamepad_output_device) {
|
||||
|
||||
case 0:
|
||||
ret = pthread_create(&logic->virt_dev_thread, NULL, virt_evdev_thread_func, (void*)(&logic->dev_stats));
|
||||
break;
|
||||
|
||||
case 1:
|
||||
ret = pthread_create(&logic->virt_dev_thread, NULL, virt_ds5_thread_func, (void*)(&logic->dev_stats));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
ret = pthread_create(&logic->virt_dev_thread, NULL, virt_ds4_thread_func, (void*)(&logic->dev_stats));
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Invalid output device specified\n");
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
logic->virt_dev_thread_running = ret == 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
int logic_copy_gamepad_status(logic_t *const logic, gamepad_status_t *const out) {
|
||||
int res = 0;
|
||||
|
||||
|
|
@ -172,7 +188,6 @@ logic_copy_gamepad_status_err:
|
|||
return res;
|
||||
}
|
||||
|
||||
|
||||
int logic_begin_status_update(logic_t *const logic) {
|
||||
int res = 0;
|
||||
|
||||
|
|
@ -188,6 +203,7 @@ logic_begin_status_update_err:
|
|||
void logic_end_status_update(logic_t *const logic) {
|
||||
pthread_mutex_unlock(&logic->gamepad_mutex);
|
||||
}
|
||||
*/
|
||||
|
||||
void logic_request_termination(logic_t *const logic) {
|
||||
logic->flags |= LOGIC_FLAGS_TERMINATION_REQUESTED;
|
||||
|
|
|
|||
99
logic.h
99
logic.h
|
|
@ -3,6 +3,7 @@
|
|||
#include "platform.h"
|
||||
#include "queue.h"
|
||||
#include "settings.h"
|
||||
#include "devices_status.h"
|
||||
|
||||
#define PRESS_AND_RELEASE_DURATION_FOR_CENTER_BUTTON_MS 80
|
||||
#define PRESS_TIME_BEFORE_CROSS_BUTTON_MS 250
|
||||
|
|
@ -12,63 +13,9 @@
|
|||
#define GAMEPAD_STATUS_FLAGS_PRESS_AND_REALEASE_CENTER 0x00000001U
|
||||
#define GAMEPAD_STATUS_FLAGS_OPEN_STEAM_QAM 0x00000002U
|
||||
|
||||
typedef struct gamepad_status {
|
||||
|
||||
int32_t joystick_positions[2][2]; // [0 left | 1 right][x axis | y axis]
|
||||
|
||||
uint8_t dpad; // 0x00 x - | 0x01 x -> | 0x02 x <- | 0x00 y - | 0x10 y ^ | 0x10 y . |
|
||||
|
||||
uint8_t l2_trigger;
|
||||
uint8_t r2_trigger;
|
||||
|
||||
uint8_t triangle;
|
||||
uint8_t circle;
|
||||
uint8_t cross;
|
||||
uint8_t square;
|
||||
|
||||
uint8_t l1;
|
||||
uint8_t r1;
|
||||
|
||||
uint8_t r3;
|
||||
uint8_t l3;
|
||||
|
||||
uint8_t option;
|
||||
uint8_t share;
|
||||
uint8_t center;
|
||||
|
||||
uint8_t l4;
|
||||
uint8_t r4;
|
||||
|
||||
uint8_t l5;
|
||||
uint8_t r5;
|
||||
|
||||
struct timeval last_gyro_motion_time;
|
||||
struct timeval last_accel_motion_time;
|
||||
|
||||
double gyro[3]; // | x, y, z| right-hand-rules -- in rad/s
|
||||
double accel[3]; // | x, y, z| positive: right, up, towards player -- in m/s^2
|
||||
|
||||
int16_t raw_gyro[3];
|
||||
int16_t raw_accel[3];
|
||||
|
||||
uint64_t rumble_events_count;
|
||||
uint8_t motors_intensity[2]; // 0 = left, 1 = right
|
||||
|
||||
volatile uint32_t flags;
|
||||
|
||||
} gamepad_status_t;
|
||||
|
||||
#define LOGIC_FLAGS_VIRT_DS4_ENABLE 0x00000001U
|
||||
#define LOGIC_FLAGS_VIRT_DS5_ENABLE 0x00000002U
|
||||
#define LOGIC_FLAGS_PLATFORM_ENABLE 0x00000010U
|
||||
#define LOGIC_FLAGS_TERMINATION_REQUESTED 0x80000000U
|
||||
|
||||
typedef enum gamepad_output {
|
||||
GAMEPAD_OUTPUT_EVDEV = 0,
|
||||
GAMEPAD_OUTPUT_DS4,
|
||||
GAMEPAD_OUTPUT_DS5,
|
||||
} gamepad_output_t;
|
||||
|
||||
typedef struct rumble_message {
|
||||
uint16_t strong_magnitude;
|
||||
uint16_t weak_magnitude;
|
||||
|
|
@ -78,21 +25,15 @@ typedef struct logic {
|
|||
|
||||
rc71l_platform_t platform;
|
||||
|
||||
pthread_mutex_t gamepad_mutex;
|
||||
gamepad_status_t gamepad;
|
||||
devices_status_t dev_stats;
|
||||
|
||||
queue_t input_queue;
|
||||
|
||||
pthread_t virt_ds4_thread;
|
||||
|
||||
pthread_t virt_ds5_thread;
|
||||
pthread_t virt_dev_thread;
|
||||
bool virt_dev_thread_running;
|
||||
|
||||
volatile uint32_t flags;
|
||||
|
||||
// the mutex is not needed if only one thread is writing this and others are checking with equality
|
||||
//pthread_mutex_t gamepad_output_mutex;
|
||||
gamepad_output_t gamepad_output;
|
||||
|
||||
queue_t rumble_events_queue;
|
||||
|
||||
controller_settings_t controller_settings;
|
||||
|
|
@ -103,12 +44,38 @@ int logic_create(logic_t *const logic);
|
|||
|
||||
int is_rc71l_ready(const logic_t *const logic);
|
||||
|
||||
void logic_request_termination(logic_t *const logic);
|
||||
|
||||
int logic_termination_requested(logic_t *const logic);
|
||||
|
||||
/**
|
||||
* This function starts the output thread for the user-chosed gamepad.
|
||||
*
|
||||
* Before starting the thread this function will also modify the status of "connected"
|
||||
* so that the started thread won't exit immediatly.
|
||||
* This function can only be called when the mutex is not needed:
|
||||
* in practice this is true when no output thread is active
|
||||
*/
|
||||
int logic_start_output_dev_thread(logic_t *const logic);
|
||||
|
||||
/**
|
||||
* This function starts the output thread for the keyboard&mouse.
|
||||
*
|
||||
* Before starting the thread this function will also modify the status of "connected"
|
||||
* so that the started thread won't exit immediatly.
|
||||
*
|
||||
* This function can only be called when the mutex is not needed:
|
||||
* in practice this is true when no output thread is active
|
||||
*/
|
||||
int logic_start_output_mouse_kbd_thread(logic_t *const logic);
|
||||
|
||||
void logic_terminate_output_thread(logic_t *const logic);
|
||||
|
||||
/*
|
||||
int logic_copy_gamepad_status(logic_t *const logic, gamepad_status_t *const out);
|
||||
|
||||
int logic_begin_status_update(logic_t *const logic);
|
||||
|
||||
void logic_end_status_update(logic_t *const logic);
|
||||
*/
|
||||
|
||||
void logic_request_termination(logic_t *const logic);
|
||||
|
||||
int logic_termination_requested(logic_t *const logic);
|
||||
144
main.c
144
main.c
|
|
@ -8,8 +8,6 @@
|
|||
logic_t global_logic;
|
||||
|
||||
static output_dev_t out_gamepadd_dev = {
|
||||
.gamepad_fd = -1,
|
||||
.imu_fd = -1,
|
||||
.logic = &global_logic,
|
||||
};
|
||||
|
||||
|
|
@ -68,6 +66,24 @@ static input_dev_t in_xbox_dev = {
|
|||
.ev_input_filter_fn = input_filter_identity,
|
||||
};
|
||||
|
||||
int start_input_devices(pthread_t *out_threads, input_dev_t **in_devs, uint16_t in_count) {
|
||||
int ret = 0;
|
||||
|
||||
uint16_t i = 0;
|
||||
for (i = 0; i < in_count; ++i) {
|
||||
const int thread_creation = pthread_create(&out_threads[i], NULL, input_dev_thread_func, (void*)(in_devs[i]));
|
||||
if (thread_creation != 0) {
|
||||
fprintf(stderr, "Error creating input thread for device %d: %d\n", (int)i, thread_creation);
|
||||
ret = -1;
|
||||
// TODO: logic_request_termination(&global_logic);
|
||||
goto start_input_devices_err;
|
||||
}
|
||||
}
|
||||
|
||||
start_input_devices_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void sig_handler(int signo)
|
||||
{
|
||||
if (signo == SIGINT) {
|
||||
|
|
@ -77,49 +93,34 @@ void sig_handler(int signo)
|
|||
}
|
||||
|
||||
int main(int argc, char ** argv) {
|
||||
int ret = 0;
|
||||
|
||||
const int logic_creation_res = logic_create(&global_logic);
|
||||
if (logic_creation_res < 0) {
|
||||
fprintf(stderr, "Unable to create logic: %d", logic_creation_res);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int imu_fd = create_output_dev("/dev/uinput", output_dev_imu);
|
||||
if (imu_fd < 0) {
|
||||
fprintf(stderr, "Unable to create IMU virtual device\n");
|
||||
return EXIT_FAILURE;
|
||||
|
||||
input_dev_t *in_devs[] = {
|
||||
&in_xbox_dev,
|
||||
&in_iio_dev,
|
||||
&in_asus_kb_1_dev,
|
||||
&in_asus_kb_2_dev,
|
||||
&in_asus_kb_3_dev,
|
||||
};
|
||||
|
||||
const uint16_t input_dev_count = (sizeof(in_devs) / sizeof(input_dev_t *));
|
||||
|
||||
pthread_t *out_threads = malloc(sizeof(pthread_t) * input_dev_count);
|
||||
|
||||
ret = start_input_devices(out_threads, in_devs, input_dev_count);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Unable to start input devices: %d -- terminating...\n", ret);
|
||||
goto input_threads_err;
|
||||
}
|
||||
|
||||
int gamepad_fd = create_output_dev("/dev/uinput", output_dev_gamepad);
|
||||
if (gamepad_fd < 0) {
|
||||
close(imu_fd);
|
||||
fprintf(stderr, "Unable to create gamepad virtual device\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int mouse_fd = create_output_dev("/dev/uinput", output_dev_mouse);
|
||||
if (mouse_fd < 0) {
|
||||
close(gamepad_fd);
|
||||
close(imu_fd);
|
||||
fprintf(stderr, "Unable to create mouse virtual device\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
out_gamepadd_dev.gamepad_fd = gamepad_fd;
|
||||
out_gamepadd_dev.imu_fd = imu_fd;
|
||||
out_gamepadd_dev.mouse_fd = mouse_fd;
|
||||
|
||||
/*
|
||||
__sighandler_t sigint_hndl = signal(SIGINT, sig_handler);
|
||||
if (sigint_hndl == SIG_ERR) {
|
||||
fprintf(stderr, "Error registering SIGINT handler\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
*/
|
||||
|
||||
int ret = 0;
|
||||
|
||||
pthread_t gamepad_thread;
|
||||
pthread_t xbox_thread, asus_kb_1_thread, asus_kb_2_thread, asus_kb_3_thread, iio_thread;
|
||||
|
||||
const int gamepad_thread_creation = pthread_create(&gamepad_thread, NULL, output_dev_thread_func, (void*)(&out_gamepadd_dev));
|
||||
if (gamepad_thread_creation != 0) {
|
||||
|
|
@ -129,69 +130,22 @@ int main(int argc, char ** argv) {
|
|||
goto gamepad_thread_err;
|
||||
}
|
||||
|
||||
const int xbox_thread_creation = pthread_create(&xbox_thread, NULL, input_dev_thread_func, (void*)(&in_xbox_dev));
|
||||
if (xbox_thread_creation != 0) {
|
||||
fprintf(stderr, "Error creating xbox input thread: %d\n", xbox_thread_creation);
|
||||
ret = -1;
|
||||
logic_request_termination(&global_logic);
|
||||
goto xbox_drv_thread_err;
|
||||
/*
|
||||
// TODO: once the application is able to exit de-comment this
|
||||
__sighandler_t sigint_hndl = signal(SIGINT, sig_handler);
|
||||
if (sigint_hndl == SIG_ERR) {
|
||||
fprintf(stderr, "Error registering SIGINT handler\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
*/
|
||||
|
||||
for (uint16_t j = 0; j < input_dev_count; ++j) {
|
||||
pthread_join(out_threads[j], NULL);
|
||||
}
|
||||
|
||||
const int asus_kb_1_thread_creation = pthread_create(&asus_kb_1_thread, NULL, input_dev_thread_func, (void*)(&in_asus_kb_1_dev));
|
||||
if (asus_kb_1_thread_creation != 0) {
|
||||
fprintf(stderr, "Error creating asus keyboard (1) input thread: %d\n", asus_kb_1_thread_creation);
|
||||
ret = -1;
|
||||
logic_request_termination(&global_logic);
|
||||
goto asus_kb_1_thread_err;
|
||||
}
|
||||
|
||||
const int asus_kb_2_thread_creation = pthread_create(&asus_kb_2_thread, NULL, input_dev_thread_func, (void*)(&in_asus_kb_2_dev));
|
||||
if (asus_kb_2_thread_creation != 0) {
|
||||
fprintf(stderr, "Error creating asus keyboard (2) input thread: %d\n", asus_kb_2_thread_creation);
|
||||
ret = -1;
|
||||
logic_request_termination(&global_logic);
|
||||
goto asus_kb_2_thread_err;
|
||||
}
|
||||
|
||||
const int asus_kb_3_thread_creation = pthread_create(&asus_kb_3_thread, NULL, input_dev_thread_func, (void*)(&in_asus_kb_3_dev));
|
||||
if (asus_kb_3_thread_creation != 0) {
|
||||
fprintf(stderr, "Error creating asus keyboard (3) input thread: %d\n", asus_kb_3_thread_creation);
|
||||
ret = -1;
|
||||
logic_request_termination(&global_logic);
|
||||
goto asus_kb_3_thread_err;
|
||||
}
|
||||
|
||||
const int iio_thread_creation = pthread_create(&iio_thread, NULL, input_dev_thread_func, (void*)(&in_iio_dev));
|
||||
if (iio_thread_creation != 0) {
|
||||
fprintf(stderr, "Error creating iio input thread: %d\n", asus_kb_3_thread_creation);
|
||||
ret = -1;
|
||||
logic_request_termination(&global_logic);
|
||||
goto iio_thread_err;
|
||||
}
|
||||
|
||||
pthread_join(iio_thread, NULL);
|
||||
|
||||
iio_thread_err:
|
||||
pthread_join(asus_kb_3_thread, NULL);
|
||||
|
||||
asus_kb_3_thread_err:
|
||||
pthread_join(asus_kb_2_thread, NULL);
|
||||
|
||||
asus_kb_2_thread_err:
|
||||
pthread_join(asus_kb_1_thread, NULL);
|
||||
|
||||
asus_kb_1_thread_err:
|
||||
pthread_join(xbox_thread, NULL);
|
||||
|
||||
xbox_drv_thread_err:
|
||||
pthread_join(gamepad_thread, NULL);
|
||||
|
||||
gamepad_thread_err:
|
||||
ioctl(gamepad_fd, UI_DEV_DESTROY);
|
||||
close(gamepad_fd);
|
||||
|
||||
// TODO: free(imu_dev.events_list);
|
||||
// TODO: free(gamepadd_dev.events_list);
|
||||
|
||||
input_threads_err:
|
||||
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
|
|
|||
844
output_dev.c
844
output_dev.c
File diff suppressed because it is too large
Load diff
51
output_dev.h
51
output_dev.h
|
|
@ -3,62 +3,11 @@
|
|||
#include "queue.h"
|
||||
#include "logic.h"
|
||||
|
||||
// Emulates a "Generic" controller:
|
||||
#define OUTPUT_DEV_NAME "ROGueENEMY"
|
||||
#define OUTPUT_DEV_VENDOR_ID 0x108c
|
||||
#define OUTPUT_DEV_PRODUCT_ID 0x0323
|
||||
#define OUTPUT_DEV_VERSION 0x0111
|
||||
|
||||
/*
|
||||
// Emulates a steam controller
|
||||
#define OUTPUT_DEV_NAME "Steam Controller"
|
||||
#define OUTPUT_DEV_VENDOR_ID 0x28de
|
||||
#define OUTPUT_DEV_PRODUCT_ID 0x1102
|
||||
#define OUTPUT_DEV_VERSION 0x0111
|
||||
#define OUTPUT_DEV_BUS_TYPE BUS_USB
|
||||
*/
|
||||
|
||||
/*
|
||||
//Emulates an Xbox one wireless controller:
|
||||
#define OUTPUT_DEV_NAME "Xbox Wireless Controller"
|
||||
#define OUTPUT_DEV_VENDOR_ID 0x045e
|
||||
#define OUTPUT_DEV_PRODUCT_ID 0x028e
|
||||
#define OUTPUT_DEV_BUS_TYPE BUS_BLUETOOTH
|
||||
*/
|
||||
|
||||
/*
|
||||
// Emulates a DualShock controller
|
||||
#define OUTPUT_DEV_NAME "Sony Interactive Entertainment DualSense Wireless Controller"
|
||||
#define OUTPUT_DEV_VENDOR_ID 0x054c
|
||||
#define OUTPUT_DEV_PRODUCT_ID 0x0ce6
|
||||
#define OUTPUT_DEV_VERSION 0x8111
|
||||
#define OUTPUT_DEV_BUS_TYPE BUS_USB
|
||||
*/
|
||||
|
||||
#define PHYS_STR "00:11:22:33:44:55"
|
||||
|
||||
#define ACCEL_RANGE 512
|
||||
#define GYRO_RANGE 2000 // max range is +/- 35 radian/s
|
||||
|
||||
#define GYRO_DEADZONE 1 // degrees/s to count as zero movement
|
||||
|
||||
#undef INCLUDE_TIMESTAMP
|
||||
#undef INCLUDE_OUTPUT_DEBUG
|
||||
|
||||
typedef enum output_dev_type {
|
||||
output_dev_gamepad,
|
||||
output_dev_imu,
|
||||
output_dev_mouse,
|
||||
} output_dev_type_t;
|
||||
|
||||
typedef struct output_dev {
|
||||
int gamepad_fd;
|
||||
int imu_fd;
|
||||
int mouse_fd;
|
||||
|
||||
logic_t *logic;
|
||||
} output_dev_t;
|
||||
|
||||
int create_output_dev(const char* uinput_path, output_dev_type_t type);
|
||||
|
||||
void *output_dev_thread_func(void *ptr);
|
||||
|
|
|
|||
22
platform.c
22
platform.c
|
|
@ -643,7 +643,7 @@ int init_platform(rc71l_platform_t *const platform) {
|
|||
|
||||
int cycle_mode(rc71l_platform_t *const platform) {
|
||||
if (platform == NULL) {
|
||||
return -1;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
char new_mode_str[] = {
|
||||
|
|
@ -659,7 +659,7 @@ int cycle_mode(rc71l_platform_t *const platform) {
|
|||
char *const dev_name = find_device(platform->udev);
|
||||
if (dev_name == NULL) {
|
||||
fprintf(stderr, "Unable to locate Asus MCU hidraw to mode-switch. Mode will not be switched.\n");
|
||||
return platform->mode;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
const int next_mode = (platform->mode + 1) % platform->modes_count;
|
||||
|
|
@ -667,13 +667,11 @@ int cycle_mode(rc71l_platform_t *const platform) {
|
|||
const int reset_res = hidraw_cycle_to_mode(dev_name, next_mode);
|
||||
if (reset_res != 0) {
|
||||
fprintf(stderr, "Unable to change mode of Asus MCU over hidraw: %d.\n", reset_res);
|
||||
}
|
||||
|
||||
free(dev_name);
|
||||
|
||||
return platform->mode;
|
||||
}
|
||||
|
||||
platform->mode = next_mode;
|
||||
return reset_res;
|
||||
} else if (platform->platform_mode == rc71l_platform_mode_asus_mcu) {
|
||||
FILE* mode_file = fopen(platform_input_path, "w");
|
||||
if (mode_file == NULL) {
|
||||
|
|
@ -685,7 +683,7 @@ int cycle_mode(rc71l_platform_t *const platform) {
|
|||
const int write_bytes = fwrite((void*)&new_mode_str[0], 1, len, mode_file);
|
||||
if (write_bytes < len) {
|
||||
fprintf(stderr, "Error writing new mode: expected to write %d bytes, %d written.\n", (int)len, (int)write_bytes);
|
||||
return -2;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
platform->mode = new_mode;
|
||||
|
|
@ -693,17 +691,9 @@ int cycle_mode(rc71l_platform_t *const platform) {
|
|||
fclose(mode_file);
|
||||
}
|
||||
|
||||
return platform->mode;
|
||||
}
|
||||
|
||||
int is_gamepad_mode(rc71l_platform_t *const platform) {
|
||||
return platform != NULL ? platform->mode == 0 : 0;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int is_mouse_mode(rc71l_platform_t *const platform) {
|
||||
return platform != NULL ? platform->mode == 1 : 0;
|
||||
}
|
||||
|
||||
int is_macro_mode(rc71l_platform_t *const platform) {
|
||||
return platform != NULL ? platform->mode == 2 : 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,8 +21,3 @@ int init_platform(rc71l_platform_t *const platform);
|
|||
int cycle_mode(rc71l_platform_t *const platform);
|
||||
|
||||
int is_mouse_mode(rc71l_platform_t *const platform);
|
||||
|
||||
int is_gamepad_mode(rc71l_platform_t *const platform);
|
||||
|
||||
int is_macro_mode(rc71l_platform_t *const platform);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
#include <stdatomic.h>
|
||||
#include <inttypes.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ void init_config(controller_settings_t *const conf) {
|
|||
conf->ff_gain = 100;
|
||||
conf->enable_qam = 0;
|
||||
conf->nintendo_layout = 0;
|
||||
conf->gamepad_output_device = 1;
|
||||
}
|
||||
|
||||
int fill_config(controller_settings_t *const conf, const char* file) {
|
||||
|
|
@ -46,6 +47,13 @@ int fill_config(controller_settings_t *const conf, const char* file) {
|
|||
fprintf(stderr, "nintendo_layout (bool) configuration not found. Default value will be used.\n");
|
||||
}
|
||||
|
||||
int gamepad_output_device;
|
||||
if (config_lookup_int(&cfg, "gamepad_output_device", &gamepad_output_device) != CONFIG_FALSE) {
|
||||
conf->gamepad_output_device = gamepad_output_device;
|
||||
} else {
|
||||
fprintf(stderr, "gamepad_output_device (int) configuration not found. Default value will be used.\n");
|
||||
}
|
||||
|
||||
config_destroy(&cfg);
|
||||
|
||||
fill_config_err:
|
||||
|
|
|
|||
|
|
@ -6,6 +6,14 @@ typedef struct controller_settings {
|
|||
uint16_t ff_gain;
|
||||
int enable_qam;
|
||||
int nintendo_layout;
|
||||
|
||||
/**
|
||||
* 0 is virtual evdev
|
||||
* 1 is DualSense
|
||||
* 2 is DualShock
|
||||
* 3 is Xbox one
|
||||
*/
|
||||
int gamepad_output_device;
|
||||
} controller_settings_t;
|
||||
|
||||
void init_config(controller_settings_t *const conf);
|
||||
|
|
|
|||
195
virt_ds4.c
195
virt_ds4.c
|
|
@ -339,7 +339,7 @@ static void destroy(int fd)
|
|||
* uhid program shouldn't do this but instead just forward the raw report.
|
||||
* However, for ducomentational purposes, we try to detect LED events here and
|
||||
* print debug messages for it. */
|
||||
static void handle_output(struct uhid_event *ev, logic_t *const logic)
|
||||
static void handle_output(struct uhid_event *ev, gamepad_status_t *const gamepad_stats)
|
||||
{
|
||||
// Rumble and LED messages are adverised via OUTPUT reports; ignore the rest
|
||||
if (ev->u.output.rtype != UHID_OUTPUT_REPORT)
|
||||
|
|
@ -397,19 +397,10 @@ static void handle_output(struct uhid_event *ev, logic_t *const logic)
|
|||
const uint8_t lightbar_blink_on = ev->u.output.data[9];
|
||||
const uint8_t lightbar_blink_off = ev->u.output.data[10];
|
||||
|
||||
if ((valid_flag0 & DS4_OUTPUT_VALID_FLAG0_MOTOR) && (logic->gamepad_output == LOGIC_FLAGS_VIRT_DS4_ENABLE)) {
|
||||
const int lock_res = pthread_mutex_lock(&logic->gamepad_mutex);
|
||||
if (lock_res != 0) {
|
||||
printf("Unable to lock gamepad mutex: %d, rumble will not be updated.\n", lock_res);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
logic->gamepad.motors_intensity[0] = motor_left;
|
||||
logic->gamepad.motors_intensity[1] = motor_right;
|
||||
++logic->gamepad.rumble_events_count;
|
||||
|
||||
pthread_mutex_unlock(&logic->gamepad_mutex);
|
||||
if ((valid_flag0 & DS4_OUTPUT_VALID_FLAG0_MOTOR)) {
|
||||
gamepad_stats->motors_intensity[0] = motor_left;
|
||||
gamepad_stats->motors_intensity[1] = motor_right;
|
||||
++gamepad_stats->rumble_events_count;
|
||||
|
||||
#if defined(VIRT_DS4_DEBUG)
|
||||
printf(
|
||||
|
|
@ -423,7 +414,7 @@ static void handle_output(struct uhid_event *ev, logic_t *const logic)
|
|||
}
|
||||
}
|
||||
|
||||
static int event(int fd, logic_t *const logic)
|
||||
static int event(int fd, gamepad_status_t *const gamepad_stats)
|
||||
{
|
||||
struct uhid_event ev;
|
||||
ssize_t ret;
|
||||
|
|
@ -469,7 +460,7 @@ static int event(int fd, logic_t *const logic)
|
|||
#if defined(VIRT_DS4_DEBUG)
|
||||
printf("UHID_OUTPUT from uhid-dev\n");
|
||||
#endif
|
||||
handle_output(&ev, logic);
|
||||
handle_output(&ev, gamepad_stats);
|
||||
break;
|
||||
case UHID_OUTPUT_EV:
|
||||
#if defined(VIRT_DS4_DEBUG)
|
||||
|
|
@ -570,42 +561,6 @@ static int event(int fd, logic_t *const logic)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t get_buttons_byte_by_gs(const gamepad_status_t *const gs) {
|
||||
uint8_t res = 0;
|
||||
|
||||
res |= gs->triangle ? 0x80 : 0x00;
|
||||
res |= gs->circle ? 0x40 : 0x00;
|
||||
res |= gs->cross ? 0x20 : 0x00;
|
||||
res |= gs->square ? 0x10 : 0x00;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static uint8_t get_buttons_byte2_by_gs(const gamepad_status_t *const gs) {
|
||||
uint8_t res = 0;
|
||||
|
||||
res |= gs->r3 ? 0x80 : 0x00;
|
||||
res |= gs->l3 ? 0x40 : 0x00;
|
||||
res |= gs->share ? 0x20 : 0x00;
|
||||
res |= gs->option ? 0x10 : 0x00;
|
||||
|
||||
res |= gs->r2_trigger > 200 ? 0x08 : 0x00;
|
||||
res |= gs->l2_trigger > 200 ? 0x04 : 0x00;
|
||||
res |= gs->r1 ? 0x02 : 0x00;
|
||||
res |= gs->l1 ? 0x01 : 0x00;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t get_buttons_byte3_by_gs(const gamepad_status_t *const gs) {
|
||||
uint8_t res = 0;
|
||||
|
||||
res |= gs->center ? 0x01 : 0x00;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
typedef enum ds4_dpad_status {
|
||||
DPAD_N = 0,
|
||||
DPAD_NE = 1,
|
||||
|
|
@ -643,13 +598,8 @@ static ds4_dpad_status_t ds4_dpad_from_gamepad(uint8_t dpad) {
|
|||
/**
|
||||
* This function arranges HID packets as described on https://www.psdevwiki.com/ps4/DS4-USB
|
||||
*/
|
||||
static int send_data(int fd, logic_t *const logic) {
|
||||
gamepad_status_t gs;
|
||||
const int gs_copy_res = logic_copy_gamepad_status(logic, &gs);
|
||||
if (gs_copy_res != 0) {
|
||||
fprintf(stderr, "Unable to copy the gamepad status: %d\n", gs_copy_res);
|
||||
return gs_copy_res;
|
||||
}
|
||||
static void compose_hid_report_buffer(int fd, gamepad_status_t *const gamepad_stats, uint8_t buf[64]) {
|
||||
gamepad_status_t gs = *gamepad_stats;
|
||||
|
||||
const int64_t time_us = gs.last_gyro_motion_time.tv_sec * 1000000 + gs.last_gyro_motion_time.tv_usec;
|
||||
|
||||
|
|
@ -702,13 +652,7 @@ static int send_data(int fd, logic_t *const logic) {
|
|||
|
||||
// see https://www.psdevwiki.com/ps4/DS4-USB
|
||||
|
||||
// [12] battery level
|
||||
uint8_t buf[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
// buf[12] battery level
|
||||
|
||||
/*
|
||||
* kernel will do:
|
||||
|
|
@ -742,21 +686,33 @@ static int send_data(int fd, logic_t *const logic) {
|
|||
const int16_t a_y = (int16_t)(-1) * gs.raw_accel[1]; // Swap Y and Z
|
||||
const int16_t a_z = (int16_t)(-1) * gs.raw_accel[2]; // Swap Y and Z
|
||||
|
||||
|
||||
buf[0] = 0x01; // [00] report ID (0x01)
|
||||
buf[1] = ((uint64_t)((int64_t)gs.joystick_positions[0][0] + (int64_t)32768) >> (uint64_t)8); // L stick, X axis
|
||||
buf[2] = ((uint64_t)((int64_t)gs.joystick_positions[0][1] + (int64_t)32768) >> (uint64_t)8); // L stick, Y axis
|
||||
buf[3] = ((uint64_t)((int64_t)gs.joystick_positions[1][0] + (int64_t)32768) >> (uint64_t)8); // R stick, X axis
|
||||
buf[4] = ((uint64_t)((int64_t)gs.joystick_positions[1][1] + (int64_t)32768) >> (uint64_t)8); // R stick, Y axis
|
||||
buf[5] = get_buttons_byte_by_gs(&gs) | (uint8_t)ds4_dpad_from_gamepad(gs.dpad);
|
||||
buf[6] = get_buttons_byte2_by_gs(&gs);
|
||||
buf[5] =
|
||||
(gs.triangle ? 0x80 : 0x00) |
|
||||
(gs.circle ? 0x40 : 0x00) |
|
||||
(gs.cross ? 0x20 : 0x00) |
|
||||
(gs.square ? 0x10 : 0x00) |
|
||||
(uint8_t)ds4_dpad_from_gamepad(gs.dpad);
|
||||
buf[6] =
|
||||
(gs.r3 ? 0x80 : 0x00) |
|
||||
(gs.l3 ? 0x40 : 0x00) |
|
||||
(gs.share ? 0x20 : 0x00) |
|
||||
(gs.option ? 0x10 : 0x00) |
|
||||
(gs.r2_trigger > 200 ? 0x08 : 0x00) |
|
||||
(gs.l2_trigger > 200 ? 0x04 : 0x00) |
|
||||
(gs.r1 ? 0x02 : 0x00) |
|
||||
(gs.l1 ? 0x01 : 0x00);
|
||||
|
||||
/*
|
||||
static uint8_t counter = 0;
|
||||
buf[7] = (((counter++) % (uint8_t)64) << ((uint8_t)2)) | get_buttons_byte3_by_gs(&gs);
|
||||
*/
|
||||
|
||||
buf[7] = get_buttons_byte3_by_gs(&gs);
|
||||
buf[7] = gs.center ? 0x01 : 0x00;
|
||||
|
||||
buf[8] = gs.l2_trigger;
|
||||
buf[9] = gs.r2_trigger;
|
||||
|
|
@ -777,6 +733,56 @@ static int send_data(int fd, logic_t *const logic) {
|
|||
buf[48] = 0x80; // IDK... it seems constant...
|
||||
buf[35] = 0x80; // IDK... it seems constant...
|
||||
buf[44] = 0x80; // IDK... it seems constant...
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread function emulating the DualShock4 controller at USB level using USB UHID ( https://www.kernel.org/doc/html/latest/hid/uhid.html ) kernel APIs.
|
||||
*
|
||||
* See https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/hid/uhid.txt?id=refs/tags/v4.10-rc3
|
||||
*/
|
||||
void *virt_ds4_thread_func(void *ptr) {
|
||||
devices_status_t *const stats = (devices_status_t*)ptr;
|
||||
|
||||
fprintf(stderr, "Open uhid-cdev %s\n", path);
|
||||
int fd = open(path, O_RDWR | O_CLOEXEC | O_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Cannot open uhid-cdev %s: %d\n", path, fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Create uhid device\n");
|
||||
int ret = create(fd);
|
||||
if (ret) {
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t buf[64];
|
||||
|
||||
for (;;) {
|
||||
usleep(1250);
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
const int lock_res = pthread_mutex_lock(&stats->mutex);
|
||||
if (lock_res != 0) {
|
||||
printf("Unable to lock gamepad mutex: %d\n", lock_res);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// main virtual device logic
|
||||
{
|
||||
event(fd, &stats->gamepad);
|
||||
|
||||
if (stats->gamepad.connected) {
|
||||
compose_hid_report_buffer(fd, &stats->gamepad, buf);
|
||||
} else {
|
||||
printf("DualShock has been terminated: closing the device.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&stats->mutex);
|
||||
|
||||
struct uhid_event l = {
|
||||
.type = UHID_INPUT2,
|
||||
|
|
@ -789,57 +795,10 @@ static int send_data(int fd, logic_t *const logic) {
|
|||
|
||||
memcpy(&l.u.input2.data[0], &buf[0], l.u.input2.size);
|
||||
|
||||
return uhid_write(fd, &l);
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread function emulating the DualShock4 controller at USB level using USB UHID ( https://www.kernel.org/doc/html/latest/hid/uhid.html ) kernel APIs.
|
||||
*
|
||||
* See https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/hid/uhid.txt?id=refs/tags/v4.10-rc3
|
||||
*/
|
||||
void *virt_ds4_thread_func(void *ptr) {
|
||||
logic_t *const logic = (logic_t*)ptr;
|
||||
|
||||
for (;;) {
|
||||
if (logic->gamepad_output != GAMEPAD_OUTPUT_DS4) {
|
||||
// sleep for 500ms before re-checking
|
||||
usleep(500000);
|
||||
continue;
|
||||
uhid_write(fd, &l);
|
||||
}
|
||||
|
||||
fprintf(stderr, "Open uhid-cdev %s\n", path);
|
||||
int fd = open(path, O_RDWR | O_CLOEXEC | O_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Cannot open uhid-cdev %s: %d\n", path, fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Create uhid device\n");
|
||||
int ret = create(fd);
|
||||
if (ret) {
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
usleep(1250);
|
||||
|
||||
event(fd, logic);
|
||||
|
||||
if (logic->gamepad_output == GAMEPAD_OUTPUT_DS4) {
|
||||
const int res = send_data(fd, logic);
|
||||
if (res < 0) {
|
||||
fprintf(stderr, "Error sending HID report: %d\n", res);
|
||||
}
|
||||
} else {
|
||||
printf("DualShock has been terminated: closing the device.\n");
|
||||
goto virt_ds4_thread_func_reset;
|
||||
}
|
||||
}
|
||||
|
||||
virt_ds4_thread_func_reset:
|
||||
destroy(fd);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
176
virt_ds5.c
176
virt_ds5.c
|
|
@ -99,7 +99,7 @@ static void destroy(int fd)
|
|||
uhid_write(fd, &ev);
|
||||
}
|
||||
|
||||
static void handle_output(struct uhid_event *ev, logic_t *const logic)
|
||||
static void handle_output(struct uhid_event *ev, gamepad_status_t *const gamepad_stats)
|
||||
{
|
||||
// Rumble and LED messages are adverised via OUTPUT reports; ignore the rest
|
||||
if (ev->u.output.rtype != UHID_OUTPUT_REPORT)
|
||||
|
|
@ -140,21 +140,11 @@ static void handle_output(struct uhid_event *ev, logic_t *const logic)
|
|||
uint8_t lightbar_green = ev->u.output.data[46];
|
||||
uint8_t lightbar_blue = ev->u.output.data[47];
|
||||
|
||||
if ((valid_flag0 & DS_OUTPUT_VALID_FLAG0_HAPTICS_SELECT) && (logic->gamepad_output == LOGIC_FLAGS_VIRT_DS5_ENABLE)) {
|
||||
if ((valid_flag0 & DS_OUTPUT_VALID_FLAG0_HAPTICS_SELECT)) {
|
||||
if ((valid_flag2 & DS_OUTPUT_VALID_FLAG2_COMPATIBLE_VIBRATION2) || (valid_flag0 & DS_OUTPUT_VALID_FLAG0_COMPATIBLE_VIBRATION)) {
|
||||
|
||||
const int lock_res = pthread_mutex_lock(&logic->gamepad_mutex);
|
||||
if (lock_res != 0) {
|
||||
printf("Unable to lock gamepad mutex: %d, rumble will not be updated.\n", lock_res);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
logic->gamepad.motors_intensity[0] = motor_left;
|
||||
logic->gamepad.motors_intensity[1] = motor_right;
|
||||
++logic->gamepad.rumble_events_count;
|
||||
|
||||
pthread_mutex_unlock(&logic->gamepad_mutex);
|
||||
gamepad_stats->motors_intensity[0] = motor_left;
|
||||
gamepad_stats->motors_intensity[1] = motor_right;
|
||||
++gamepad_stats->rumble_events_count;
|
||||
|
||||
#if defined(VIRT_DS5_DEBUG)
|
||||
printf(
|
||||
|
|
@ -169,7 +159,7 @@ static void handle_output(struct uhid_event *ev, logic_t *const logic)
|
|||
}
|
||||
}
|
||||
|
||||
static int event(int fd, logic_t *const logic)
|
||||
static int event(int fd, gamepad_status_t *const gamepad_stats)
|
||||
{
|
||||
struct uhid_event ev;
|
||||
ssize_t ret;
|
||||
|
|
@ -215,7 +205,7 @@ static int event(int fd, logic_t *const logic)
|
|||
#if defined(VIRT_DS5_DEBUG)
|
||||
printf("UHID_OUTPUT from uhid-dev\n");
|
||||
#endif
|
||||
handle_output(&ev, logic);
|
||||
handle_output(&ev, gamepad_stats);
|
||||
break;
|
||||
case UHID_OUTPUT_EV:
|
||||
#if defined(VIRT_DS5_DEBUG)
|
||||
|
|
@ -293,42 +283,6 @@ static int event(int fd, logic_t *const logic)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t get_buttons_byte_by_gs(const gamepad_status_t *const gs) {
|
||||
uint8_t res = 0;
|
||||
|
||||
res |= gs->triangle ? 0x80 : 0x00;
|
||||
res |= gs->circle ? 0x40 : 0x00;
|
||||
res |= gs->cross ? 0x20 : 0x00;
|
||||
res |= gs->square ? 0x10 : 0x00;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static uint8_t get_buttons_byte2_by_gs(const gamepad_status_t *const gs) {
|
||||
uint8_t res = 0;
|
||||
|
||||
res |= gs->r3 ? 0x80 : 0x00;
|
||||
res |= gs->l3 ? 0x40 : 0x00;
|
||||
res |= gs->share ? 0x20 : 0x00;
|
||||
res |= gs->option ? 0x10 : 0x00;
|
||||
|
||||
res |= gs->r2_trigger > 200 ? 0x08 : 0x00;
|
||||
res |= gs->l2_trigger > 200 ? 0x04 : 0x00;
|
||||
res |= gs->r1 ? 0x02 : 0x00;
|
||||
res |= gs->l1 ? 0x01 : 0x00;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t get_buttons_byte3_by_gs(const gamepad_status_t *const gs) {
|
||||
uint8_t res = 0;
|
||||
|
||||
res |= gs->center ? 0x01 : 0x00;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
typedef enum ds5_dpad_status {
|
||||
DPAD_N = 0,
|
||||
DPAD_NE = 1,
|
||||
|
|
@ -363,13 +317,8 @@ static ds5_dpad_status_t ds5_dpad_from_gamepad(uint8_t dpad) {
|
|||
return DPAD_RELEASED;
|
||||
}
|
||||
|
||||
static int send_data(int fd, logic_t *const logic) {
|
||||
gamepad_status_t gs;
|
||||
const int gs_copy_res = logic_copy_gamepad_status(logic, &gs);
|
||||
if (gs_copy_res != 0) {
|
||||
fprintf(stderr, "Unable to copy the gamepad status: %d\n", gs_copy_res);
|
||||
return gs_copy_res;
|
||||
}
|
||||
static void compose_hid_report_buffer(int fd, gamepad_status_t *const gamepad_stats, uint8_t buf[64]) {
|
||||
gamepad_status_t gs = *gamepad_stats;
|
||||
|
||||
static uint8_t seq_num = 0x00;
|
||||
|
||||
|
|
@ -409,13 +358,6 @@ static int send_data(int fd, logic_t *const logic) {
|
|||
|
||||
const uint32_t timestamp = sim_time + (int)((double)empty_reports * DS5_SPEC_DELTA_TIME);
|
||||
|
||||
uint8_t buf[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
const int16_t g_x = gs.raw_gyro[0];
|
||||
const int16_t g_y = (int16_t)(-1) * gs.raw_gyro[1]; // Swap Y and Z
|
||||
const int16_t g_z = (int16_t)(-1) * gs.raw_gyro[2]; // Swap Y and Z
|
||||
|
|
@ -472,6 +414,56 @@ static int send_data(int fd, logic_t *const logic) {
|
|||
buf[48] = 0x80; // IDK... it seems constant...
|
||||
buf[35] = 0x80; // IDK... it seems constant...
|
||||
buf[44] = 0x80; // IDK... it seems constant...
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread function emulating the DualSense controller at USB level using USB UHID ( https://www.kernel.org/doc/html/latest/hid/uhid.html ) kernel APIs.
|
||||
*
|
||||
* See https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/hid/uhid.txt?id=refs/tags/v4.10-rc3
|
||||
*/
|
||||
void *virt_ds5_thread_func(void *ptr) {
|
||||
devices_status_t *const stats = (devices_status_t*)ptr;
|
||||
|
||||
fprintf(stderr, "Open uhid-cdev %s\n", path);
|
||||
int fd = open(path, O_RDWR | O_CLOEXEC | O_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Cannot open uhid-cdev %s: %d\n", path, fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Create uhid device\n");
|
||||
int ret = create(fd);
|
||||
if (ret) {
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t buf[64];
|
||||
|
||||
for (;;) {
|
||||
usleep(1250);
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
const int lock_res = pthread_mutex_lock(&stats->mutex);
|
||||
if (lock_res != 0) {
|
||||
printf("Unable to lock gamepad mutex: %d\n", lock_res);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// main virtual device logic
|
||||
{
|
||||
event(fd, &stats->gamepad);
|
||||
|
||||
if (stats->gamepad.connected) {
|
||||
compose_hid_report_buffer(fd, &stats->gamepad, buf);
|
||||
} else {
|
||||
printf("DualSense has been terminated: closing the device.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&stats->mutex);
|
||||
|
||||
struct uhid_event l = {
|
||||
.type = UHID_INPUT2,
|
||||
|
|
@ -484,58 +476,10 @@ static int send_data(int fd, logic_t *const logic) {
|
|||
|
||||
memcpy(&l.u.input2.data[0], &buf[0], l.u.input2.size);
|
||||
|
||||
return uhid_write(fd, &l);
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread function emulating the DualSense controller at USB level using USB UHID ( https://www.kernel.org/doc/html/latest/hid/uhid.html ) kernel APIs.
|
||||
*
|
||||
* See https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/hid/uhid.txt?id=refs/tags/v4.10-rc3
|
||||
*/
|
||||
void *virt_ds5_thread_func(void *ptr) {
|
||||
logic_t *const logic = (logic_t*)ptr;
|
||||
|
||||
for (;;) {
|
||||
if (logic->gamepad_output != GAMEPAD_OUTPUT_DS5) {
|
||||
// sleep for 500ms before re-checking
|
||||
usleep(500000);
|
||||
continue;
|
||||
uhid_write(fd, &l);
|
||||
}
|
||||
|
||||
fprintf(stderr, "Open uhid-cdev %s\n", path);
|
||||
int fd = open(path, O_RDWR | O_CLOEXEC | O_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Cannot open uhid-cdev %s: %d\n", path, fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Create uhid device\n");
|
||||
int ret = create(fd);
|
||||
if (ret) {
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
usleep(1250);
|
||||
|
||||
event(fd, logic);
|
||||
|
||||
if (logic->gamepad_output == GAMEPAD_OUTPUT_DS5) {
|
||||
const int res = send_data(fd, logic);
|
||||
if (res < 0) {
|
||||
fprintf(stderr, "Error sending HID report: %d\n", res);
|
||||
}
|
||||
} else {
|
||||
printf("DualSense has been terminated: closing the device.\n");
|
||||
goto virt_ds5_thread_func_reset;
|
||||
}
|
||||
}
|
||||
|
||||
virt_ds5_thread_func_reset:
|
||||
destroy(fd);
|
||||
}
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
570
virt_evdev.c
Normal file
570
virt_evdev.c
Normal file
|
|
@ -0,0 +1,570 @@
|
|||
#include "virt_evdev.h"
|
||||
#include "devices_status.h"
|
||||
#include "logic.h"
|
||||
|
||||
static const char* uinput_path = "/dev/uinput";
|
||||
|
||||
static int create_gamepad_uinput() {
|
||||
int fd = open(uinput_path, O_WRONLY | O_NONBLOCK);
|
||||
if(fd < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_gamepad_uinput_err;
|
||||
}
|
||||
|
||||
struct uinput_setup dev = {0};
|
||||
strncpy(dev.name, OUTPUT_DEV_NAME, UINPUT_MAX_NAME_SIZE-1);
|
||||
|
||||
#if defined(OUTPUT_DEV_BUS_TYPE)
|
||||
dev.id.bustype = OUTPUT_DEV_BUS_TYPE;
|
||||
#else
|
||||
dev.id.bustype = BUS_VIRTUAL;
|
||||
#endif
|
||||
dev.id.vendor = OUTPUT_DEV_VENDOR_ID;
|
||||
dev.id.product = OUTPUT_DEV_PRODUCT_ID;
|
||||
|
||||
#if defined(OUTPUT_DEV_VERSION)
|
||||
dev.id.version = OUTPUT_DEV_VERSION;
|
||||
#endif
|
||||
|
||||
//if (ioctl(fd, /*UI_SET_PHYS_STR*/ 18, PHYS_STR) != 0) {
|
||||
// fprintf(stderr, "Controller and gyroscope will NOT be recognized as a single device!\n");
|
||||
//}
|
||||
|
||||
if (ioctl(fd, UI_SET_PHYS, PHYS_STR) != 0) {
|
||||
fprintf(stderr, "Error setting the phys of the virtual controller.\n");
|
||||
}
|
||||
|
||||
#if !defined(UI_SET_PHYS_STR)
|
||||
#warning Will not change the PHYS
|
||||
#endif
|
||||
|
||||
#if !defined(UI_SET_UNIQ_STR)
|
||||
#warning Will not change the UNIQ
|
||||
#endif
|
||||
|
||||
#if defined(UI_SET_PHYS_STR)
|
||||
ioctl(fd, UI_SET_PHYS_STR(18), PHYS_STR);
|
||||
#else
|
||||
fprintf(stderr, "UI_SET_PHYS_STR unavailable.\n");
|
||||
#endif
|
||||
|
||||
#if defined(UI_SET_UNIQ_STR)
|
||||
ioctl(fd, UI_SET_UNIQ_STR(18), PHYS_STR);
|
||||
#else
|
||||
fprintf(stderr, "UI_SET_UNIQ_STR unavailable.\n");
|
||||
#endif
|
||||
|
||||
//ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_BUTTONPAD);
|
||||
ioctl(fd, UI_SET_EVBIT, EV_ABS);
|
||||
ioctl(fd, UI_SET_EVBIT, EV_KEY);
|
||||
ioctl(fd, UI_SET_EVBIT, EV_SYN);
|
||||
#if defined(INCLUDE_TIMESTAMP)
|
||||
ioctl(fd, UI_SET_EVBIT, EV_MSC);
|
||||
ioctl(fd, UI_SET_MSCBIT, MSC_TIMESTAMP);
|
||||
#endif
|
||||
|
||||
ioctl(fd, UI_SET_ABSBIT, ABS_X);
|
||||
ioctl(fd, UI_SET_ABSBIT, ABS_Y);
|
||||
ioctl(fd, UI_SET_ABSBIT, ABS_Z);
|
||||
ioctl(fd, UI_SET_ABSBIT, ABS_RX);
|
||||
ioctl(fd, UI_SET_ABSBIT, ABS_RY);
|
||||
ioctl(fd, UI_SET_ABSBIT, ABS_RZ);
|
||||
ioctl(fd, UI_SET_ABSBIT, ABS_HAT0X);
|
||||
ioctl(fd, UI_SET_ABSBIT, ABS_HAT0Y);
|
||||
ioctl(fd, UI_SET_ABSBIT, ABS_HAT2X);
|
||||
ioctl(fd, UI_SET_ABSBIT, ABS_HAT2Y);
|
||||
|
||||
ioctl(fd, UI_SET_KEYBIT, BTN_SOUTH);
|
||||
ioctl(fd, UI_SET_KEYBIT, BTN_EAST);
|
||||
ioctl(fd, UI_SET_KEYBIT, BTN_NORTH);
|
||||
ioctl(fd, UI_SET_KEYBIT, BTN_WEST);
|
||||
ioctl(fd, UI_SET_KEYBIT, BTN_TL);
|
||||
ioctl(fd, UI_SET_KEYBIT, BTN_TR);
|
||||
ioctl(fd, UI_SET_KEYBIT, BTN_TL2);
|
||||
ioctl(fd, UI_SET_KEYBIT, BTN_TR2);
|
||||
ioctl(fd, UI_SET_KEYBIT, BTN_SELECT);
|
||||
ioctl(fd, UI_SET_KEYBIT, BTN_START);
|
||||
ioctl(fd, UI_SET_KEYBIT, BTN_MODE);
|
||||
ioctl(fd, UI_SET_KEYBIT, BTN_THUMBL);
|
||||
ioctl(fd, UI_SET_KEYBIT, BTN_THUMBR);
|
||||
ioctl(fd, UI_SET_KEYBIT, BTN_GEAR_DOWN);
|
||||
ioctl(fd, UI_SET_KEYBIT, BTN_GEAR_UP);
|
||||
ioctl(fd, UI_SET_KEYBIT, BTN_DPAD_UP);
|
||||
ioctl(fd, UI_SET_KEYBIT, BTN_DPAD_DOWN);
|
||||
ioctl(fd, UI_SET_KEYBIT, BTN_DPAD_LEFT);
|
||||
ioctl(fd, UI_SET_KEYBIT, BTN_DPAD_RIGHT);
|
||||
|
||||
const struct uinput_abs_setup devAbsX = {
|
||||
.code = ABS_X,
|
||||
.absinfo = {
|
||||
.value = 0,
|
||||
.minimum = -32768,
|
||||
.maximum = +32768,
|
||||
.resolution = 1,
|
||||
.fuzz = 16,
|
||||
.flat = 128,
|
||||
}
|
||||
};
|
||||
if(ioctl(fd, UI_ABS_SETUP, &devAbsX) < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_gamepad_uinput_err;
|
||||
}
|
||||
|
||||
struct uinput_abs_setup devAbsY = {
|
||||
.code = ABS_Y,
|
||||
.absinfo = {
|
||||
.value = 0,
|
||||
.minimum = -32768,
|
||||
.maximum = +32768,
|
||||
.resolution = 1,
|
||||
.fuzz = 16,
|
||||
.flat = 128,
|
||||
}
|
||||
};
|
||||
|
||||
if(ioctl(fd, UI_ABS_SETUP, &devAbsY) < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_gamepad_uinput_err;
|
||||
}
|
||||
|
||||
struct uinput_abs_setup devAbsZ = {
|
||||
.code = ABS_Z,
|
||||
.absinfo = {
|
||||
.value = 0,
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.resolution = 1,
|
||||
//.fuzz = 16,
|
||||
//.flat = 128,
|
||||
}
|
||||
};
|
||||
if(ioctl(fd, UI_ABS_SETUP, &devAbsZ) < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_gamepad_uinput_err;
|
||||
}
|
||||
|
||||
struct uinput_abs_setup devAbsRX = {
|
||||
.code = ABS_RX,
|
||||
.absinfo = {
|
||||
.value = 0,
|
||||
.minimum = -32768,
|
||||
.maximum = +32768,
|
||||
.resolution = 1,
|
||||
.fuzz = 16,
|
||||
.flat = 128,
|
||||
}
|
||||
};
|
||||
if(ioctl(fd, UI_ABS_SETUP, &devAbsRX) < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_gamepad_uinput_err;
|
||||
}
|
||||
|
||||
struct uinput_abs_setup devAbsRY = {
|
||||
.code = ABS_RY,
|
||||
.absinfo = {
|
||||
.value = -1,
|
||||
.minimum = -32768,
|
||||
.maximum = +32768,
|
||||
.resolution = 1,
|
||||
.fuzz = 16,
|
||||
.flat = 128,
|
||||
}
|
||||
};
|
||||
if(ioctl(fd, UI_ABS_SETUP, &devAbsRY) < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_gamepad_uinput_err;
|
||||
}
|
||||
|
||||
struct uinput_abs_setup devAbsRZ = {
|
||||
.code = ABS_RZ,
|
||||
.absinfo = {
|
||||
.value = 0,
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.resolution = 1,
|
||||
//.fuzz = 16,
|
||||
//.flat = 128,
|
||||
}
|
||||
};
|
||||
if(ioctl(fd, UI_ABS_SETUP, &devAbsRZ) < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_gamepad_uinput_err;
|
||||
}
|
||||
|
||||
struct uinput_abs_setup devAbsHat0X = {
|
||||
.code = ABS_HAT0X,
|
||||
.absinfo = {
|
||||
.value = 0,
|
||||
.minimum = -1,
|
||||
.maximum = 1,
|
||||
.resolution = 1,
|
||||
//.fuzz = 16,
|
||||
//.flat = 128,
|
||||
}
|
||||
};
|
||||
if(ioctl(fd, UI_ABS_SETUP, &devAbsHat0X) < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_gamepad_uinput_err;
|
||||
}
|
||||
|
||||
struct uinput_abs_setup devAbsHat0Y = {
|
||||
.code = ABS_HAT0Y,
|
||||
.absinfo = {
|
||||
.value = 0,
|
||||
.minimum = -1,
|
||||
.maximum = 1,
|
||||
.resolution = 1,
|
||||
//.fuzz = 16,
|
||||
//.flat = 128,
|
||||
}
|
||||
};
|
||||
if(ioctl(fd, UI_ABS_SETUP, &devAbsHat0Y) < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_gamepad_uinput_err;
|
||||
}
|
||||
|
||||
struct uinput_abs_setup devAbsHat2X = {
|
||||
.code = ABS_HAT2X,
|
||||
.absinfo = {
|
||||
.value = 0,
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.resolution = 51,
|
||||
//.fuzz = 16,
|
||||
//.flat = 128,
|
||||
}
|
||||
};
|
||||
if(ioctl(fd, UI_ABS_SETUP, &devAbsHat2X) < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_gamepad_uinput_err;
|
||||
}
|
||||
|
||||
struct uinput_abs_setup devAbsHat2Y = {
|
||||
.code = ABS_HAT2Y,
|
||||
.absinfo = {
|
||||
.value = 0,
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.resolution = 51,
|
||||
//.fuzz = 16,
|
||||
//.flat = 128,
|
||||
}
|
||||
};
|
||||
if(ioctl(fd, UI_ABS_SETUP, &devAbsHat2Y) < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_gamepad_uinput_err;
|
||||
}
|
||||
|
||||
if(ioctl(fd, UI_DEV_SETUP, &dev) < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_gamepad_uinput_err;
|
||||
}
|
||||
|
||||
if(ioctl(fd, UI_DEV_CREATE) < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_gamepad_uinput_err;
|
||||
}
|
||||
|
||||
create_gamepad_uinput_err:
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int create_imu_uinput() {
|
||||
int fd = open(uinput_path, O_WRONLY | O_NONBLOCK);
|
||||
if(fd < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_imu_uinput_err;
|
||||
}
|
||||
|
||||
struct uinput_setup dev = {0};
|
||||
strncpy(dev.name, OUTPUT_DEV_NAME, UINPUT_MAX_NAME_SIZE-1);
|
||||
|
||||
#if defined(OUTPUT_DEV_BUS_TYPE)
|
||||
dev.id.bustype = OUTPUT_DEV_BUS_TYPE;
|
||||
#else
|
||||
dev.id.bustype = BUS_VIRTUAL;
|
||||
#endif
|
||||
dev.id.vendor = OUTPUT_DEV_VENDOR_ID;
|
||||
dev.id.product = OUTPUT_DEV_PRODUCT_ID;
|
||||
|
||||
#if defined(OUTPUT_DEV_VERSION)
|
||||
dev.id.version = OUTPUT_DEV_VERSION;
|
||||
#endif
|
||||
|
||||
//if (ioctl(fd, /*UI_SET_PHYS_STR*/ 18, PHYS_STR) != 0) {
|
||||
// fprintf(stderr, "Controller and gyroscope will NOT be recognized as a single device!\n");
|
||||
//}
|
||||
|
||||
if (ioctl(fd, UI_SET_PHYS, PHYS_STR) != 0) {
|
||||
fprintf(stderr, "Error setting the phys of the virtual controller.\n");
|
||||
}
|
||||
|
||||
#if !defined(UI_SET_PHYS_STR)
|
||||
#warning Will not change the PHYS
|
||||
#endif
|
||||
|
||||
#if !defined(UI_SET_UNIQ_STR)
|
||||
#warning Will not change the UNIQ
|
||||
#endif
|
||||
|
||||
#if defined(UI_SET_PHYS_STR)
|
||||
ioctl(fd, UI_SET_PHYS_STR(18), PHYS_STR);
|
||||
#else
|
||||
fprintf(stderr, "UI_SET_PHYS_STR unavailable.\n");
|
||||
#endif
|
||||
|
||||
#if defined(UI_SET_UNIQ_STR)
|
||||
ioctl(fd, UI_SET_UNIQ_STR(18), PHYS_STR);
|
||||
#else
|
||||
fprintf(stderr, "UI_SET_UNIQ_STR unavailable.\n");
|
||||
#endif
|
||||
|
||||
ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_ACCELEROMETER);
|
||||
ioctl(fd, UI_SET_EVBIT, EV_ABS);
|
||||
#if defined(INCLUDE_TIMESTAMP)
|
||||
ioctl(fd, UI_SET_EVBIT, EV_MSC);
|
||||
ioctl(fd, UI_SET_MSCBIT, MSC_TIMESTAMP);
|
||||
#endif
|
||||
|
||||
ioctl(fd, UI_SET_ABSBIT, ABS_X);
|
||||
ioctl(fd, UI_SET_ABSBIT, ABS_Y);
|
||||
ioctl(fd, UI_SET_ABSBIT, ABS_Z);
|
||||
ioctl(fd, UI_SET_ABSBIT, ABS_RX);
|
||||
ioctl(fd, UI_SET_ABSBIT, ABS_RY);
|
||||
ioctl(fd, UI_SET_ABSBIT, ABS_RZ);
|
||||
|
||||
//ioctl(fd, UI_SET_KEYBIT, BTN_TRIGGER);
|
||||
//ioctl(fd, UI_SET_KEYBIT, BTN_THUMB);
|
||||
|
||||
struct uinput_abs_setup devAbsX = {0};
|
||||
devAbsX.code = ABS_X;
|
||||
devAbsX.absinfo.minimum = -ACCEL_RANGE;
|
||||
devAbsX.absinfo.maximum = ACCEL_RANGE;
|
||||
devAbsX.absinfo.resolution = 255; // 255 units = 1g
|
||||
devAbsX.absinfo.fuzz = 5;
|
||||
devAbsX.absinfo.flat = 0;
|
||||
if(ioctl(fd, UI_ABS_SETUP, &devAbsX) < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_imu_uinput_err;
|
||||
}
|
||||
|
||||
struct uinput_abs_setup devAbsY = {0};
|
||||
devAbsY.code = ABS_Y;
|
||||
devAbsY.absinfo.minimum = -ACCEL_RANGE;
|
||||
devAbsY.absinfo.maximum = ACCEL_RANGE;
|
||||
devAbsY.absinfo.resolution = 255; // 255 units = 1g
|
||||
devAbsY.absinfo.fuzz = 5;
|
||||
devAbsY.absinfo.flat = 0;
|
||||
if(ioctl(fd, UI_ABS_SETUP, &devAbsY) < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_imu_uinput_err;
|
||||
}
|
||||
|
||||
struct uinput_abs_setup devAbsZ = {0};
|
||||
devAbsZ.code = ABS_Z;
|
||||
devAbsZ.absinfo.minimum = -ACCEL_RANGE;
|
||||
devAbsZ.absinfo.maximum = ACCEL_RANGE;
|
||||
devAbsZ.absinfo.resolution = 255; // 255 units = 1g
|
||||
devAbsZ.absinfo.fuzz = 5;
|
||||
devAbsZ.absinfo.flat = 0;
|
||||
if(ioctl(fd, UI_ABS_SETUP, &devAbsZ) < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_imu_uinput_err;
|
||||
}
|
||||
|
||||
struct uinput_abs_setup devAbsRX = {0};
|
||||
devAbsRX.code = ABS_RX;
|
||||
devAbsRX.absinfo.minimum = -GYRO_RANGE;
|
||||
devAbsRX.absinfo.maximum = GYRO_RANGE;
|
||||
devAbsRX.absinfo.resolution = 1; // 1 unit = 1 degree/s
|
||||
devAbsRX.absinfo.fuzz = 0;
|
||||
devAbsRX.absinfo.flat = GYRO_DEADZONE;
|
||||
if(ioctl(fd, UI_ABS_SETUP, &devAbsRX) < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_imu_uinput_err;
|
||||
}
|
||||
|
||||
struct uinput_abs_setup devAbsRY = {0};
|
||||
devAbsRY.code = ABS_RY;
|
||||
devAbsRY.absinfo.minimum = -GYRO_RANGE;
|
||||
devAbsRY.absinfo.maximum = GYRO_RANGE;
|
||||
devAbsRY.absinfo.resolution = 1; // 1 unit = 1 degree/s
|
||||
devAbsRY.absinfo.fuzz = 0;
|
||||
devAbsRY.absinfo.flat = GYRO_DEADZONE;
|
||||
if(ioctl(fd, UI_ABS_SETUP, &devAbsRY) < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_imu_uinput_err;
|
||||
}
|
||||
|
||||
struct uinput_abs_setup devAbsRZ = {0};
|
||||
devAbsRZ.code = ABS_RZ;
|
||||
devAbsRZ.absinfo.minimum = -GYRO_RANGE;
|
||||
devAbsRZ.absinfo.maximum = GYRO_RANGE;
|
||||
devAbsRZ.absinfo.resolution = 1; // 1 unit = 1 degree/s
|
||||
devAbsRZ.absinfo.fuzz = 0;
|
||||
devAbsRZ.absinfo.flat = GYRO_DEADZONE;
|
||||
if(ioctl(fd, UI_ABS_SETUP, &devAbsRZ) < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_imu_uinput_err;
|
||||
}
|
||||
|
||||
if(ioctl(fd, UI_DEV_SETUP, &dev) < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_imu_uinput_err;
|
||||
}
|
||||
|
||||
if(ioctl(fd, UI_DEV_CREATE) < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_imu_uinput_err;
|
||||
}
|
||||
|
||||
create_imu_uinput_err:
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
void *virt_evdev_thread_func(void *ptr) {
|
||||
devices_status_t *const stats = (devices_status_t*)ptr;
|
||||
|
||||
const int gamepad_fd = create_gamepad_uinput();
|
||||
if (gamepad_fd != 0) {
|
||||
fprintf(stderr, "Unable to create the virtual evdev gamepad device: %d\n", gamepad_fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const int imu_fd = create_imu_uinput();
|
||||
if (gamepad_fd != 0) {
|
||||
fprintf(stderr, "Unable to create the virtual evdev imu device: %d\n", gamepad_fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
usleep(1250);
|
||||
|
||||
const int lock_res = pthread_mutex_lock(&stats->mutex);
|
||||
if (lock_res != 0) {
|
||||
printf("Unable to lock gamepad mutex: %d\n", lock_res);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// main virtual device logic
|
||||
{
|
||||
//event(fd, &stats->gamepad);
|
||||
|
||||
if (stats->gamepad.connected) {
|
||||
// TODO: do whatever it takes...
|
||||
} else {
|
||||
printf("kbd&mouse has been terminated: closing the device.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&stats->mutex);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
static void emit_ev(output_dev_t *const out_dev, const message_t *const msg) {
|
||||
// if events are flagged as do not emit... Do NOT emit!
|
||||
if (msg->flags & INPUT_FILTER_FLAGS_DO_NOT_EMIT) {
|
||||
return;
|
||||
}
|
||||
|
||||
int fd = out_dev->gamepad_fd;
|
||||
const uint32_t msg_flags = msg->data.event.ev_flags;
|
||||
if ((msg_flags & EV_MESSAGE_FLAGS_IMU) != 0) {
|
||||
fd = out_dev->imu_fd;
|
||||
} else if ((msg_flags & EV_MESSAGE_FLAGS_MOUSE) != 0) {
|
||||
fd = out_dev->mouse_fd;
|
||||
} else {
|
||||
fd = out_dev->gamepad_fd;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < msg->data.event.ev_count; ++i) {
|
||||
struct input_event ev = {
|
||||
.code = msg->data.event.ev[i].code,
|
||||
.type = msg->data.event.ev[i].type,
|
||||
.value = msg->data.event.ev[i].value,
|
||||
.time = msg->data.event.ev[i].time,
|
||||
};
|
||||
|
||||
if ((msg_flags & EV_MESSAGE_FLAGS_PRESERVE_TIME) == 0) {
|
||||
gettimeofday(&ev.time, NULL);
|
||||
}
|
||||
|
||||
#if defined(INCLUDE_OUTPUT_DEBUG)
|
||||
printf(
|
||||
"Output: Received event %s (%s): %d\n",
|
||||
libevdev_event_type_get_name(ev.type),
|
||||
libevdev_event_code_get_name(ev.type, ev.code),
|
||||
ev.value
|
||||
);
|
||||
#endif
|
||||
|
||||
const ssize_t written = write(fd, (void*)&ev, sizeof(ev));
|
||||
if (written != sizeof(ev)) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Error writing event %s %s %d: written %ld bytes out of %ld\n",
|
||||
libevdev_event_type_get_name(ev.type),
|
||||
libevdev_event_code_get_name(ev.type, ev.code),
|
||||
ev.value,
|
||||
written,
|
||||
sizeof(ev)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(INCLUDE_TIMESTAMP)
|
||||
gettimeofday(&now, NULL);
|
||||
const struct input_event timestamp_ev = {
|
||||
.code = MSC_TIMESTAMP,
|
||||
.type = EV_MSC,
|
||||
.value = (now.tv_sec - secAtInit)*1000000 + (now.tv_usec - usecAtInit),
|
||||
.time = now,
|
||||
};
|
||||
const ssize_t timestamp_written = write(fd, (void*)×tamp_ev, sizeof(timestamp_ev));
|
||||
if (timestamp_written != sizeof(timestamp_ev)) {
|
||||
fprintf(stderr, "Error in sync: written %ld bytes out of %ld\n", timestamp_written, sizeof(timestamp_ev));
|
||||
}
|
||||
#endif
|
||||
|
||||
struct timeval now = {0};
|
||||
gettimeofday(&now, NULL);
|
||||
const struct input_event syn_ev = {
|
||||
.code = SYN_REPORT,
|
||||
.type = EV_SYN,
|
||||
.value = 0,
|
||||
.time = now,
|
||||
};
|
||||
const ssize_t sync_written = write(fd, (void*)&syn_ev, sizeof(syn_ev));
|
||||
if (sync_written != sizeof(syn_ev)) {
|
||||
fprintf(stderr, "Error in sync: written %ld bytes out of %ld\n", sync_written, sizeof(syn_ev));
|
||||
}
|
||||
}
|
||||
*/
|
||||
47
virt_evdev.h
Normal file
47
virt_evdev.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
|
||||
#include "logic.h"
|
||||
|
||||
#undef VIRT_EVDEV_DEBUG
|
||||
|
||||
|
||||
// Emulates a "Generic" controller:
|
||||
#define OUTPUT_DEV_NAME "ROGueENEMY"
|
||||
#define OUTPUT_DEV_VENDOR_ID 0x108c
|
||||
#define OUTPUT_DEV_PRODUCT_ID 0x0323
|
||||
#define OUTPUT_DEV_VERSION 0x0111
|
||||
|
||||
/*
|
||||
// Emulates a steam controller
|
||||
#define OUTPUT_DEV_NAME "Steam Controller"
|
||||
#define OUTPUT_DEV_VENDOR_ID 0x28de
|
||||
#define OUTPUT_DEV_PRODUCT_ID 0x1102
|
||||
#define OUTPUT_DEV_VERSION 0x0111
|
||||
#define OUTPUT_DEV_BUS_TYPE BUS_USB
|
||||
*/
|
||||
|
||||
/*
|
||||
//Emulates an Xbox one wireless controller:
|
||||
#define OUTPUT_DEV_NAME "Xbox Wireless Controller"
|
||||
#define OUTPUT_DEV_VENDOR_ID 0x045e
|
||||
#define OUTPUT_DEV_PRODUCT_ID 0x028e
|
||||
#define OUTPUT_DEV_BUS_TYPE BUS_BLUETOOTH
|
||||
*/
|
||||
|
||||
/*
|
||||
// Emulates a DualShock controller
|
||||
#define OUTPUT_DEV_NAME "Sony Interactive Entertainment DualSense Wireless Controller"
|
||||
#define OUTPUT_DEV_VENDOR_ID 0x054c
|
||||
#define OUTPUT_DEV_PRODUCT_ID 0x0ce6
|
||||
#define OUTPUT_DEV_VERSION 0x8111
|
||||
#define OUTPUT_DEV_BUS_TYPE BUS_USB
|
||||
*/
|
||||
|
||||
#define PHYS_STR "00:11:22:33:44:55"
|
||||
|
||||
#define ACCEL_RANGE 512
|
||||
#define GYRO_RANGE 2000 // max range is +/- 35 radian/s
|
||||
|
||||
#define GYRO_DEADZONE 1 // degrees/s to count as zero movement
|
||||
|
||||
void *virt_evdev_thread_func(void *ptr);
|
||||
123
virt_mouse_kbd.c
Normal file
123
virt_mouse_kbd.c
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
#include "virt_mouse_kbd.h"
|
||||
|
||||
static const char* uinput_path = "/dev/uinput";
|
||||
|
||||
static int create_mouse_uinput() {
|
||||
int fd = open(uinput_path, O_WRONLY | O_NONBLOCK);
|
||||
if(fd < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_mouse_uinput_err;
|
||||
}
|
||||
|
||||
struct uinput_setup dev = {0};
|
||||
strncpy(dev.name, OUTPUT_DEV_NAME, UINPUT_MAX_NAME_SIZE-1);
|
||||
|
||||
#if defined(OUTPUT_DEV_BUS_TYPE)
|
||||
dev.id.bustype = OUTPUT_DEV_BUS_TYPE;
|
||||
#else
|
||||
dev.id.bustype = BUS_VIRTUAL;
|
||||
#endif
|
||||
dev.id.vendor = OUTPUT_DEV_VENDOR_ID;
|
||||
dev.id.product = OUTPUT_DEV_PRODUCT_ID;
|
||||
|
||||
#if defined(OUTPUT_DEV_VERSION)
|
||||
dev.id.version = OUTPUT_DEV_VERSION;
|
||||
#endif
|
||||
|
||||
//if (ioctl(fd, /*UI_SET_PHYS_STR*/ 18, PHYS_STR) != 0) {
|
||||
// fprintf(stderr, "Controller and gyroscope will NOT be recognized as a single device!\n");
|
||||
//}
|
||||
|
||||
if (ioctl(fd, UI_SET_PHYS, PHYS_STR) != 0) {
|
||||
fprintf(stderr, "Error setting the phys of the virtual controller.\n");
|
||||
}
|
||||
|
||||
#if !defined(UI_SET_PHYS_STR)
|
||||
#warning Will not change the PHYS
|
||||
#endif
|
||||
|
||||
#if !defined(UI_SET_UNIQ_STR)
|
||||
#warning Will not change the UNIQ
|
||||
#endif
|
||||
|
||||
ioctl(fd, UI_SET_EVBIT, EV_REL);
|
||||
ioctl(fd, UI_SET_EVBIT, EV_KEY);
|
||||
ioctl(fd, UI_SET_EVBIT, EV_MSC);
|
||||
ioctl(fd, UI_SET_EVBIT, EV_SYN);
|
||||
#if defined(INCLUDE_TIMESTAMP)
|
||||
ioctl(fd, UI_SET_MSCBIT, MSC_TIMESTAMP);
|
||||
#endif
|
||||
|
||||
ioctl(fd, UI_SET_KEYBIT, BTN_LEFT);
|
||||
ioctl(fd, UI_SET_KEYBIT, BTN_MIDDLE);
|
||||
ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT);
|
||||
//ioctl(fd, UI_SET_KEYBIT, BTN_SIDE);
|
||||
//ioctl(fd, UI_SET_KEYBIT, BTN_EXTRA);
|
||||
|
||||
ioctl(fd, UI_SET_RELBIT, REL_X);
|
||||
ioctl(fd, UI_SET_RELBIT, REL_Y);
|
||||
ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
|
||||
ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES);
|
||||
|
||||
if(ioctl(fd, UI_DEV_SETUP, &dev) < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_mouse_uinput_err;
|
||||
}
|
||||
|
||||
if(ioctl(fd, UI_DEV_CREATE) < 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
goto create_mouse_uinput_err;
|
||||
}
|
||||
|
||||
create_mouse_uinput_err:
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int create_kb_uinput() {
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
void *virt_mouse_kbd_thread_func(void *ptr) {
|
||||
devices_status_t *const stats = (devices_status_t*)ptr;
|
||||
|
||||
const int mouse_fd = create_mouse_uinput();
|
||||
if (mouse_fd != 0) {
|
||||
fprintf(stderr, "Unable to create the mouse virtual evdev: %d\n", mouse_fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
keyboard_status_t prev_kbd_stats;
|
||||
kbd_status_init(&prev_kbd_stats);
|
||||
|
||||
for (;;) {
|
||||
usleep(1250);
|
||||
|
||||
const int lock_res = pthread_mutex_lock(&stats->mutex);
|
||||
if (lock_res != 0) {
|
||||
printf("Unable to lock gamepad mutex: %d\n", lock_res);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// main virtual device logic
|
||||
{
|
||||
//event(fd, &stats->gamepad);
|
||||
|
||||
if (stats->kbd.connected) {
|
||||
// TODO: do whatever it takes...
|
||||
} else {
|
||||
printf("kbd&mouse has been terminated: closing the device.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&stats->mutex);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
16
virt_mouse_kbd.h
Normal file
16
virt_mouse_kbd.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include "logic.h"
|
||||
|
||||
#undef VIRT_EVDEV_DEBUG
|
||||
|
||||
|
||||
// Emulates a "Generic" controller:
|
||||
#define OUTPUT_DEV_NAME "ROGueENEMY"
|
||||
#define OUTPUT_DEV_VENDOR_ID 0x108c
|
||||
#define OUTPUT_DEV_PRODUCT_ID 0x0323
|
||||
#define OUTPUT_DEV_VERSION 0x0111
|
||||
|
||||
#define PHYS_STR "00:11:22:33:44:55"
|
||||
|
||||
void *virt_mouse_kbd_thread_func(void *ptr);
|
||||
Loading…
Add table
Add a link
Reference in a new issue