Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace raw mode with calibration feature #50

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,16 @@ add at the moment. To stop the program just kill it in any way you want.
Seperate virtual controllers are created for each one plugged into the adapter
and hotplugging (both controllers and adapters) is supported.

To calibrate input ranges, first run with the `--calibrate` flag and push your
sticks and shoulder buttons all the way in every direction. Quit the program
and run it again using the given calibration string as the argument for
`--set-calibration-data`.

Quirks
------
* It's new, so there might be bugs! Please report them!
* The uinput kernel module is required. If it's not autoloaded, you should do
so with `modprobe uinput`
* Input ranges on the sticks/analog triggers are scaled to try to match the
physical ranges of the controls. To remove this scaling run the program with
the `--raw` flag.
* If all your controllers start messing with the mouse cursor, you can fix
them with this xorg.conf rule. (You can place it in a file in xorg.conf.d)

Expand Down
136 changes: 98 additions & 38 deletions wii-u-gc-adapter.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ const int AXIS_OFFSET_VALUES[6] = {
ABS_RZ
};

static uint8_t AXIS_MIN_VALUES[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
static uint8_t AXIS_MAX_VALUES[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static bool calibrate = false;

struct ff_event
{
bool in_use;
Expand Down Expand Up @@ -98,8 +102,6 @@ struct adapter
struct adapter *next;
};

static bool raw_mode;

static volatile int quitting;

static struct adapter adapters;
Expand Down Expand Up @@ -154,23 +156,27 @@ static bool uinput_create(int i, struct ports *port, unsigned char type)
ioctl(port->uinput, UI_SET_ABSBIT, ABS_Z);
ioctl(port->uinput, UI_SET_ABSBIT, ABS_RZ);

if (raw_mode)
{
uinput_dev.absmin[ABS_X] = 0; uinput_dev.absmax[ABS_X] = 255;
uinput_dev.absmin[ABS_Y] = 0; uinput_dev.absmax[ABS_Y] = 255;
uinput_dev.absmin[ABS_RX] = 0; uinput_dev.absmax[ABS_RX] = 255;
uinput_dev.absmin[ABS_RY] = 0; uinput_dev.absmax[ABS_RY] = 255;
uinput_dev.absmin[ABS_Z] = 0; uinput_dev.absmax[ABS_Z] = 255;
uinput_dev.absmin[ABS_RZ] = 0; uinput_dev.absmax[ABS_RZ] = 255;
}
else
{
uinput_dev.absmin[ABS_X] = 20; uinput_dev.absmax[ABS_X] = 235;
uinput_dev.absmin[ABS_Y] = 20; uinput_dev.absmax[ABS_Y] = 235;
uinput_dev.absmin[ABS_RX] = 30; uinput_dev.absmax[ABS_RX] = 225;
uinput_dev.absmin[ABS_RY] = 30; uinput_dev.absmax[ABS_RY] = 225;
uinput_dev.absmin[ABS_Z] = 25; uinput_dev.absmax[ABS_Z] = 225;
uinput_dev.absmin[ABS_RZ] = 25; uinput_dev.absmax[ABS_RZ] = 225;
if (calibrate) {
uinput_dev.absmin[ABS_X] = 0; uinput_dev.absmax[ABS_X] = 0xFF;
uinput_dev.absmin[ABS_Y] = 0; uinput_dev.absmax[ABS_Y] = 0xFF;
uinput_dev.absmin[ABS_RX] = 0; uinput_dev.absmax[ABS_RX] = 0xFF;
uinput_dev.absmin[ABS_RY] = 0; uinput_dev.absmax[ABS_RY] = 0xFF;
uinput_dev.absmin[ABS_Z] = 0; uinput_dev.absmax[ABS_Z] = 0xFF;
uinput_dev.absmin[ABS_RZ] = 0; uinput_dev.absmax[ABS_RZ] = 0xFF;
} else {
uinput_dev.absmin[ABS_X] = AXIS_MIN_VALUES[AXIS_OFFSET_VALUES[ABS_X]];
uinput_dev.absmin[ABS_Y] = AXIS_MIN_VALUES[AXIS_OFFSET_VALUES[ABS_Y]];
uinput_dev.absmin[ABS_RX] = AXIS_MIN_VALUES[AXIS_OFFSET_VALUES[ABS_RX]];
uinput_dev.absmin[ABS_RY] = AXIS_MIN_VALUES[AXIS_OFFSET_VALUES[ABS_RY]];
uinput_dev.absmin[ABS_Z] = AXIS_MIN_VALUES[AXIS_OFFSET_VALUES[ABS_Z]];
uinput_dev.absmin[ABS_RZ] = AXIS_MIN_VALUES[AXIS_OFFSET_VALUES[ABS_RZ]];

uinput_dev.absmax[ABS_X] = AXIS_MAX_VALUES[AXIS_OFFSET_VALUES[ABS_X]];
uinput_dev.absmax[ABS_Y] = AXIS_MAX_VALUES[AXIS_OFFSET_VALUES[ABS_Y]];
uinput_dev.absmax[ABS_RX] = AXIS_MAX_VALUES[AXIS_OFFSET_VALUES[ABS_RX]];
uinput_dev.absmax[ABS_RY] = AXIS_MAX_VALUES[AXIS_OFFSET_VALUES[ABS_RY]];
uinput_dev.absmax[ABS_Z] = AXIS_MAX_VALUES[AXIS_OFFSET_VALUES[ABS_Z]];
uinput_dev.absmax[ABS_RZ] = AXIS_MAX_VALUES[AXIS_OFFSET_VALUES[ABS_RZ]];
}

// rumble
Expand Down Expand Up @@ -382,6 +388,15 @@ static void handle_payload(int i, struct ports *port, unsigned char *payload, st
if (AXIS_OFFSET_VALUES[j] == ABS_Y || AXIS_OFFSET_VALUES[j] == ABS_RY)
value ^= 0xFF; // flip from 0 - 255 to 255 - 0

if (calibrate) {
if (value < AXIS_MIN_VALUES[j]) {
AXIS_MIN_VALUES[j] = value;
}
if (value > AXIS_MAX_VALUES[j]) {
AXIS_MAX_VALUES[j] = value;
}
}

if (port->axis[j] != value)
{
events[e_count].type = EV_ABS;
Expand Down Expand Up @@ -461,6 +476,14 @@ static void handle_payload(int i, struct ports *port, unsigned char *payload, st
}
}

void print_calibration_data()
{
for (size_t i = 0; i < sizeof(AXIS_MIN_VALUES); i++) {
printf("%d,%d,", AXIS_MIN_VALUES[i], AXIS_MAX_VALUES[i]);
}
printf("\n");
}

static void *adapter_thread(void *data)
{
struct adapter *a = (struct adapter *)data;
Expand Down Expand Up @@ -530,6 +553,10 @@ static void *adapter_thread(void *data)
break;
}
}

if (calibrate) {
print_calibration_data();
}
}

for (int i = 0; i < 4; i++)
Expand Down Expand Up @@ -637,42 +664,75 @@ enum {
};

static struct option options[] = {
{ "raw", no_argument, 0, 'r' },
{ "calibrate", no_argument, 0, 'c' },
{ "set-calibration-data", required_argument, 0, 's' },
{ "vendor", required_argument, 0, opt_vendor },
{ "product", required_argument, 0, opt_product },
{ 0, 0, 0, 0 },
};

int main(int argc, char *argv[])
void set_calibration_data(char str[])
{
struct udev *udev;
struct udev_device *uinput;
struct sigaction sa;
char *token = strtok(str, ",");
for (size_t i = 0; token && i < sizeof(AXIS_MIN_VALUES); i++) {
AXIS_MIN_VALUES[i] = atoi(token);
token = strtok(NULL, ",");

memset(&sa, 0, sizeof(sa));
AXIS_MAX_VALUES[i] = atoi(token);
token = strtok(NULL, ",");
}
}

while (1) {
void parse_args(int argc, char *argv[])
{
bool has_calibration_data = false;
for (;;) {
int option_index = 0;
int c = getopt_long(argc, argv, "r", options, &option_index);
if (c == -1)
break;

switch (c) {
case 'r':
fprintf(stderr, "raw mode enabled\n");
raw_mode = true;
break;
case opt_vendor:
vendor_id = parse_id(optarg);
fprintf(stderr, "vendor_id = %#06x\n", vendor_id);
break;
case opt_product:
product_id = parse_id(optarg);
fprintf(stderr, "product_id = %#06x\n", product_id);
break;
case 'c':
fprintf(stderr, "displaying calibration data, quit to display the results\n");
calibrate = true;
break;
case 's':
set_calibration_data(optarg);
has_calibration_data = true;
break;
case opt_vendor:
vendor_id = parse_id(optarg);
fprintf(stderr, "vendor_id = %#06x\n", vendor_id);
break;
case opt_product:
product_id = parse_id(optarg);
fprintf(stderr, "product_id = %#06x\n", product_id);
break;
}
}

if (!has_calibration_data && !calibrate) {
memset(&AXIS_MIN_VALUES, 0, sizeof(AXIS_MIN_VALUES));
memset(&AXIS_MAX_VALUES, 0xFF, sizeof(AXIS_MAX_VALUES));
}

if (!calibrate) {
printf("using calibration data: ");
print_calibration_data();
}
}

int main(int argc, char *argv[])
{
struct udev *udev;
struct udev_device *uinput;
struct sigaction sa;

memset(&sa, 0, sizeof(sa));

parse_args(argc, argv);

sa.sa_handler = quitting_signal;
sa.sa_flags = SA_RESTART | SA_RESETHAND;
sigemptyset(&sa.sa_mask);
Expand Down