Some more work on ini file parsing and refactoring command line parsing

This commit is contained in:
Ingo Ruhnke 2010-08-09 17:38:17 +02:00
parent e1393fb40c
commit 96626bb5c0
15 changed files with 644 additions and 265 deletions

View file

@ -79,8 +79,8 @@ Interface 0:
Interface 1:
Endpoint 3(in): Headset mic
Endpoint 4(out): Headset phone
Endpoint 5(in): UNKNOWN
Endpoint 5(out): Information comes in when Headset gets plugged in
Endpoint 5(in): Headset status information
Endpoint 5(out): Headset configuration information
Interface 2:
Endpoint 6(in): Chatpad
Interface 3:
@ -92,6 +92,7 @@ len: 3 data: 0x01 0x03 0x0e // current LED status
len: 3 data: 0x02 0x03 0x00 // UNKNOWN: maybe headset connection status or volume
len: 3 data: 0x03 0x03 0x03 // Rumble Status (0x00 in the last pos means rumble is disabled, 0x03 is default)
len: 3 data: 0x08 0x03 0x00 // Headset connection status (0x00 no headset, 0x02 headset)
// also chatpad status, but requires sending 0x1f before its reported
len: 20 data: 0x00 0x14 0x00 0x00 0x00 0x00 0x69 0xed 0x23 0xff 0x6b 0x00 0x15 0x03 0x00 0x00 0x00 0x00 0x00 0x00
len: 20 data: 0x00 0x14 0x00 0x00 0x00 0x00 0xfc 0xec 0x23 0xff 0x6b 0x00 0x15 0x03 0x00 0x00 0x00 0x00 0x00 0x00
@ -188,20 +189,27 @@ len: 4 data: 0x00 0x04 0x00 0x00
---------
Sending to interface 1, endpoint 5:
This only works with the headset plugged in:
This only works with the headset plugged in, these settings seem to be
permanent, so handle with care:
0x00 0x03 0x0N -> { 0x05, 0x03, 0x0N } - sets something to N, with N [0, 1]
0x01 0x03 0x0N -> { 0x01, 0x03, 0x0N } - sets something to N, with N [0, 1]
0x02 0x03 0x0N -> { 0x02, 0x03, 0x0N } - sets something to N, with N [0, f]
0x02 0x03 0x0N -> { 0x02, 0x03, 0x0N } - sets something to N, with N [0, f] (seems to be volume control)
0x03 0x05 ???? -> { 0x03, 0x05, 0xff, 0x00, 0x00 } - doesn't seem to set anything, just reply
0x04 ???? ????-> { 0x04, 0x03, 0xff } - doesn't seem to set anything, just reply
$ ./usbdebug 0x045e:0x028e
listen 5
send 5 0x00 0x03 0x01
send 5 0x01 0x03 0x01
send 5 0x02 0x03 0x0f
0x04 0x04 0x00 0x00 seems to cause status reply
---------
Plugging in a headset (interface 0, endpoint 1): len: 3 data: 0x08 0x03 0x00
Pulling out a headset (interface 0, endpoint 1): len: 3 data: 0x08 0x03 0x02
Headset sends with 8192 bytes per second
The headset might be using isochronous transfer, neither bulk nor interrupt, libusb doesn't support that yet I think
@ -226,6 +234,10 @@ usb_control_msg()'s
0x41 0x0 0x0a 0x02 # light circle
0x41 0x0 0x0b 0x02 # light people
0x41 0x0 0x0c 0x02 # light backlight leds
0x41 0x0 0x0d 0x02 #
0x41 0x0 0x0e 0x02 #
0x41 0x0 0x0f 0x02 #
0x41 0x0 0x10 0x02 # send at start
0x41 0x0 0x11 0x02 # light capslock
0x41 0x0 0x12 0x02 # light square
0x41 0x0 0x13 0x02 # light square and capslock
@ -233,7 +245,7 @@ usb_control_msg()'s
0x41 0x0 0x15 0x02 # light capslock and circle
0x41 0x0 0x16 0x02 # light circle and square
0x41 0x0 0x17 0x02 # light circle, square and capslock
0x41 0x0 0x18 0x02 #
0x41 0x0 0x18 0x02 # send at start
0x41 0x0 0x19 0x02 #
0x41 0x0 0x1a 0x02 #
0x41 0x0 0x1b 0x02 # makes led go on on a keypress

20
TODO
View file

@ -15,6 +15,26 @@ git push --tags
Stuff to do before 0.6.0 release:
=================================
* ini support
* endpoints can be taken directly from the usb configuration, thus
avoiding issues with nummeric endpoints
* bcdDevice (product release version) seem to indicate type:
new controller with merged endpoints: bcdDevice: 0x0114
old ones: 1.10
better way then to check for endpoints maybe
* give a hint in the documentation on how to do multi line comments:
xboxdrv \
--buttonmap A=X,X=A `# swap A and X` \
--buttonmap B=Y,Y=B `# swap B and Y`
* 045e:028f is the play&charge kit, give an error message when somebody tries to use that
* axis-min/max range must be transmitted to AxisEvent (maybe normalize to float)
* match by protocol not, just vendor/product, from xpad.c:

View file

@ -196,8 +196,8 @@ public:
callback(data.get());
}
// if (endpoint == 6)
// std::cout << "Clear Halt: " << libusb_clear_halt(m_handle, endpoint) << std::endl;
if (endpoint == 6)
std::cout << "Clear Halt: " << libusb_clear_halt(m_handle, LIBUSB_ENDPOINT_IN | endpoint) << std::endl;
}
break;

View file

@ -614,16 +614,19 @@ button events will come out wrong.
Changes the descriptive name the device will have
.TP
\*(T<\fB\-\-square\-axis\fR\*(T>
The Xbox360 gamepad, as most other current day gamepads, features a
circular movment range, which restricts the movement so that the
distance to the center never gets beyond 1. This means that when you
have the controller at the top/left the value reported is (0.7, 0.7)
(i.e. length 1, angle 45) instead of (1,1). This behaviour is
different then most classic joysticks, which had a square range and
allowed x and y to be handled completly indepened.
The Xbox360 gamepad, as most other current day gamepads,
features a circular movement range, which restricts the
movement so that the distance to the center never gets
beyond 1. This means that when you have the controller
at the top/left the value reported is (0.7, 0.7)
(i.e. length 1, angle 45) instead of (1,1). This
behaviour is different then most classic PC joysticks,
which had a square range and would report (1,1) when
hold in the top/left corner.
Some old games (i.e. DOS stuff) require a square movement range and
will thus not function properly with the Xbox360 gamepad. Via the
Some old games (i.e. mostly DOS stuff) require a
square movement range and will not function properly
with the Xbox360 gamepad. Via the
\*(T<\fB\-\-square\-axis\fR\*(T> option you can work around this issue and diagonals will
be reported as (1,1).
.TP

View file

@ -13,10 +13,15 @@ static struct usb_device_id chatpad_table [] = {
{ }
};
MODULE_DEVICE_TABLE (usb, chatpad_table);
MODULE_DEVICE_TABLE(usb, chatpad_table);
//unsigned char init_sequence[] = { 0x1f, 0x1e, 0x1b, 0x1b, 0x1f, 0x18, 0x10, 0x13 };
//unsigned char init_sequence[] = { 0x1f, 0x1b, 0x1b, 0x1f, 0x18, 0x10, 0x13 };
unsigned char init_sequence[] = { 0x1f, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b };
struct usb_chatpad {
struct usb_device* udev;
struct usb_interface* intf;
struct urb* chatpad_urb;
unsigned char* chatpad_idata;
@ -28,11 +33,13 @@ struct usb_chatpad {
struct delayed_work worker;
int ctrl_ready;
int flip_flop;
int counter;
};
static void chatpad_chatpad_cb(struct urb *urb)
static void chatpad_idata_cb(struct urb *urb)
{
struct usb_chatpad* chatpad = urb->context;
printk(KERN_INFO "chatpad_chatpad_cb()\n");
switch (urb->status)
{
@ -59,6 +66,15 @@ static void chatpad_chatpad_cb(struct urb *urb)
break;
}
{
//struct usb_endpoint_descriptor* ep = &chatpad->intf->cur_altsetting->endpoint[0].desc;
//usb_reset_endpoint(urb->dev, usb_rcvintpipe(urb->dev, ep->bEndpointAddress));
//extern int usb_clear_halt(struct usb_device *dev, int pipe);
int retval = usb_reset_configuration(chatpad->udev);
printk(KERN_INFO "usb_reset(): %d\n", retval);
}
{
int retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)
@ -67,6 +83,39 @@ static void chatpad_chatpad_cb(struct urb *urb)
}
}
static void chatpad_control_cb(struct urb *urb)
{
struct usb_chatpad* chatpad = urb->context;
printk(KERN_INFO "chatpad_control_cb()\n");
switch (urb->status)
{
case 0:
{
int i = 0;
printk(KERN_INFO "chatpad_control_cb(): XXXX------------XXXXX %d - ", urb->actual_length);
for(i = 0; i < urb->actual_length; ++i)
{
printk("0x%02x ", (int)(((unsigned char*)urb->transfer_buffer)[i]));
}
printk("\n");
}
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN: // triggered when the module get unloaded or device disconnected
printk(KERN_INFO "chatpad_chatpad_cb(): fail1 %d\n", urb->status);
return;
default:
printk(KERN_INFO "chatpad_chatpad_cb(): fail2 %d\n", urb->status);
break;
}
schedule_delayed_work(&chatpad->worker, msecs_to_jiffies(1000));
}
static void chatpad_setup_chatpad_readloop(struct usb_chatpad* chatpad, struct usb_interface *intf)
{
printk(KERN_INFO "chatpad_setup_chatpad_readloop()\n");
@ -83,7 +132,7 @@ static void chatpad_setup_chatpad_readloop(struct usb_chatpad* chatpad, struct u
usb_fill_int_urb(chatpad->chatpad_urb, udev,
usb_rcvintpipe(udev, ep->bEndpointAddress),
chatpad->chatpad_idata, 32,
chatpad_chatpad_cb, chatpad, ep->bInterval);
chatpad_idata_cb, chatpad, ep->bInterval);
chatpad->chatpad_urb->transfer_dma = chatpad->chatpad_idata_dma;
chatpad->chatpad_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
@ -126,41 +175,30 @@ static void chatpad_send_ctrl_msg(struct usb_chatpad* chatpad, int value)
}
}
static void chatpad_send_ctrl_msg_cb(struct urb *urb)
{
printk(KERN_INFO "chatpad_send_ctrl_msg_cb()\n");
switch (urb->status)
{
default:
printk(KERN_INFO "chatpad_send_ctrl_msg_cb(): status = %d\n", urb->status);
break;
}
}
static void chatpad_send_ctrl_msg_async(struct usb_chatpad* chatpad, int value)
{
printk(KERN_INFO "chatpad_sending: 0x%x\n", value);
chatpad->control_cr.bRequestType = USB_TYPE_VENDOR | USB_RECIP_INTERFACE;
chatpad->control_cr.bRequest = USB_REQ_GET_STATUS;
chatpad->control_cr.wValue = cpu_to_le16(value);
chatpad->control_cr.wIndex = cpu_to_le16(2);
chatpad->control_cr.wLength = cpu_to_le16(0);
// FIXME: must use different urb!
usb_fill_control_urb(chatpad->control_urb,
chatpad->udev,
usb_sndctrlpipe(chatpad->udev, 0),
(unsigned char*)&chatpad->control_cr,
NULL, // transfer_buffer
0, // buffer_length
chatpad_send_ctrl_msg_cb,
chatpad_control_cb,
chatpad);
{
int retval = usb_submit_urb(chatpad->control_urb, GFP_ATOMIC);
if (retval)
{
printk(KERN_INFO "RETVAL: %d\n", retval);
printk(KERN_INFO "chatpad_send_ctrl_msg_async: retval = %d\n", retval);
}
}
}
@ -169,20 +207,17 @@ static void chatpad_chatpad_keepalive(struct work_struct *work)
{
struct usb_chatpad* chatpad = container_of(work, struct usb_chatpad, worker.work);
if (chatpad->flip_flop)
if (chatpad->counter < sizeof(init_sequence))
{
printk(KERN_INFO "chatpad_sending: 0x1f()\n");
chatpad_send_ctrl_msg(chatpad, 0x1f);
chatpad_send_ctrl_msg_async(chatpad, init_sequence[chatpad->counter]);
}
else
{
printk(KERN_INFO "chatpad_sending: 0x1e()\n");
chatpad_send_ctrl_msg(chatpad, 0x1e);
chatpad_send_ctrl_msg_async(chatpad, (chatpad->counter % 2) ? 0x1f : 0x1e);
}
chatpad->counter += 1;
chatpad->flip_flop = !chatpad->flip_flop;
schedule_delayed_work(&chatpad->worker, msecs_to_jiffies(1000));
printk(KERN_INFO "chatpad_chatpad_keepalive: waiting for reply\n");
}
static void chatpad_setup_chatpad_keepalive(struct usb_chatpad* chatpad, struct usb_interface *intf)
@ -202,6 +237,8 @@ static void chatpad_setup_chatpad_keepalive(struct usb_chatpad* chatpad, struct
static int chatpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
printk(KERN_INFO "chatpad_probe() -------------------------------------------------------------\n");
printk(KERN_INFO "chatpad_probe() called: %04x:%04x - %d - %d %d %d\n",
(int)id->idVendor, (int)id->idProduct, intf->minor,
@ -213,6 +250,7 @@ static int chatpad_probe(struct usb_interface *intf, const struct usb_device_id
{
struct usb_chatpad* chatpad = kzalloc(sizeof(struct usb_chatpad), GFP_KERNEL);
chatpad->udev = udev;
chatpad->intf = intf;
usb_set_intfdata(intf, chatpad);
chatpad_setup_chatpad_readloop(chatpad, intf);
@ -253,7 +291,7 @@ static struct usb_driver chatpad_driver = {
};
static int __init usb_chatpad_init(void)
{
{
// FIXME: called once for each interface?!
int result = usb_register(&chatpad_driver);

View file

@ -20,6 +20,7 @@
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <stdexcept>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
@ -27,15 +28,16 @@
#include "arg_parser.hpp"
#include "helper.hpp"
#include "uinput_deviceid.hpp"
#include "ini_schema_builder.hpp"
#include "ini_parser.hpp"
#include "options.hpp"
#include "ini_schema.hpp"
#include "uinput_deviceid.hpp"
#define RAISE_EXCEPTION(x) do { \
std::ostringstream kiJk8f08d4oMX; \
kiJk8f08d4oMX << x; \
throw std::runtime_error(kiJk8f08d4oMX.str()); \
} while(0)
#define RAISE_EXCEPTION(x) do { \
std::ostringstream kiJk8f08d4oMX; \
kiJk8f08d4oMX << x; \
throw std::runtime_error(kiJk8f08d4oMX.str()); \
} while(0)
Options* g__options = 0;
@ -46,6 +48,8 @@ enum {
OPTION_QUIET,
OPTION_SILENT,
OPTION_DAEMON,
OPTION_CONFIG,
OPTION_WRITE_CONFIG,
OPTION_TEST_RUMBLE,
OPTION_RUMBLE,
OPTION_QUIT,
@ -92,9 +96,16 @@ enum {
};
CommandLineParser::CommandLineParser() :
argp()
m_argp(),
m_ini()
{
argp
init_argp();
}
void
CommandLineParser::init_argp()
{
m_argp
.add_usage("[OPTION]...")
.add_text("Xbox360 USB Gamepad Userspace Driver")
.add_newline()
@ -115,6 +126,8 @@ CommandLineParser::CommandLineParser() :
.add_option(OPTION_NO_EXTRA_DEVICES, 0, "no-extra-devices", "", "Do not create keyboard and mouse devices, just use a single device")
.add_option(OPTION_MIMIC_XPAD, 0, "mimic-xpad", "", "Causes xboxdrv to use the same axis and button names as the xpad kernel driver")
.add_option(OPTION_DAEMON, 'D', "daemon", "", "run as daemon")
.add_option(OPTION_CONFIG, 'c', "config", "FILE", "read configuration from FILE")
.add_option(OPTION_WRITE_CONFIG, 0, "write-config", "FILE", "write an example configuration to FILE")
.add_newline()
.add_text("Device Options: ")
@ -163,7 +176,66 @@ CommandLineParser::CommandLineParser() :
.add_newline()
.add_text("See README for more documentation and examples.")
.add_text("Report bugs to Ingo Ruhnke <grumbel@gmx.de>");
.add_text("Report bugs to Ingo Ruhnke <grumbel@gmx.de>");
}
void
CommandLineParser::init_ini(Options* opts)
{
m_ini.clear();
m_ini.section("xboxdrv")
("verbose", &opts->verbose)
("silent", &opts->silent)
("quiet", &opts->quiet)
("rumble", &opts->rumble)
("led", &opts->led)
("rumble-l", &opts->rumble_l)
("rumble-r", &opts->rumble_r)
("rumble-gain", &opts->rumble_gain)
("controller-id", &opts->controller_id)
("wireless-id", &opts->wireless_id)
("instant-exit", &opts->instant_exit)
("no-uinput", &opts->no_uinput)
("detach-kernel-driver", &opts->detach_kernel_driver)
("busid", &opts->busid)
("devid", &opts->devid)
("deadzone", &opts->deadzone)
("deadzone-trigger", &opts->deadzone_trigger)
("vendor-id", &opts->vendor_id)
("product-id", &opts->product_id)
("square-axis", &opts->square_axis)
("four-way-restrictor", &opts->four_way_restrictor)
("dpad-rotation", &opts->dpad_rotation)
("evdev-device", &opts->evdev_device);
m_ini.section("uinput")
("device-name", &opts->uinput_config.device_name)
("trigger-as-button", &opts->uinput_config.trigger_as_button)
("trigger-as-zaxis", &opts->uinput_config.trigger_as_zaxis)
("dpad-as-button", &opts->uinput_config.dpad_as_button)
("dpad-only", &opts->uinput_config.dpad_only)
("force-feedback", &opts->uinput_config.force_feedback)
("extra-devices", &opts->uinput_config.extra_devices)
// mimic_xpad()
// btn_map
// axis_map
;
m_ini.section("ui-buttonmap", boost::bind(&CommandLineParser::set_ui_buttonmap, this, _1, _2));
m_ini.section("ui-axismap", boost::bind(&CommandLineParser::set_ui_axismap, this, _1, _2));
m_ini.section("buttonmap", boost::bind(&CommandLineParser::set_buttonmap, this, _1, _2));
m_ini.section("axismap", boost::bind(&CommandLineParser::set_axismap, this, _1, _2));
m_ini.section("autofire", boost::bind(&CommandLineParser::set_autofire, this, _1, _2));
m_ini.section("relative-axis", boost::bind(&CommandLineParser::set_relative_axis, this, _1, _2));
m_ini.section("calibration", boost::bind(&CommandLineParser::set_calibration, this, _1, _2));
m_ini.section("axis-sensitivity", boost::bind(&CommandLineParser::set_calibration, this, _1, _2));
m_ini.section("evdev-absmap", boost::bind(&CommandLineParser::set_evdev_absmap, this, _1, _2));
m_ini.section("evdev-keymap", boost::bind(&CommandLineParser::set_evdev_keymap, this, _1, _2));
}
void set_ui_button_map(ButtonMap& ui_button_map, const std::string& str)
@ -195,7 +267,8 @@ void set_ui_button_map(ButtonMap& ui_button_map, const std::string& str)
}
}
void set_ui_axis_map(AxisEvent* ui_axis_map, const std::string& str)
void
CommandLineParser::set_ui_axismap_from_string(const std::string& str)
{
std::string::size_type i = str.find_first_of('=');
if (i == std::string::npos)
@ -204,18 +277,9 @@ void set_ui_axis_map(AxisEvent* ui_axis_map, const std::string& str)
}
else
{
XboxAxis axis = string2axis(str.substr(0, i));
AxisEvent event = AxisEvent::from_string(str.substr(i+1, str.size()-i));
if (axis != XBOX_AXIS_UNKNOWN)
{
ui_axis_map[axis] = event;
}
else
{
throw std::runtime_error("Couldn't convert string \"" + str + "\" to ui-axis-mapping");
}
}
set_axismap(str.substr(0, i),
str.substr(i+1, str.size()-i));
}
}
void set_evdev_absmap(std::map<int, XboxAxis>& absmap, const std::string& str)
@ -233,10 +297,40 @@ void set_evdev_keymap(std::map<int, XboxButton>& keymap, const std::string& str)
std::cout << "KEY: " << str2key(lhs) << std::endl;
}
template<class C, class Func>
void arg2vector2(const std::string& str, typename std::vector<C>& lst, Func func)
{
std::string::const_iterator start = str.begin();
for(std::string::const_iterator i = str.begin(); i != str.end(); ++i)
{
if (*i == ',')
{
if (i != start)
{
std::string lhs, rhs;
split_string_at(std::string(start, i), '=', &lhs, &rhs);
lst.push_back(func(lhs, rhs));
}
start = i+1;
}
}
if (start != str.end())
{
std::string lhs, rhs;
split_string_at(std::string(start, str.end()), '=', &lhs, &rhs);
lst.push_back(func(lhs, rhs));
}
}
void
CommandLineParser::parse_args(int argc, char** argv, Options* options)
{
ArgParser::ParsedOptions parsed = argp.parse_args(argc, argv);
init_ini(options);
m_options = options;
ArgParser::ParsedOptions parsed = m_argp.parse_args(argc, argv);
for(ArgParser::ParsedOptions::const_iterator i = parsed.begin(); i != parsed.end(); ++i)
{
@ -270,6 +364,43 @@ CommandLineParser::parse_args(int argc, char** argv, Options* options)
opts.mode = Options::RUN_DAEMON;
break;
case OPTION_WRITE_CONFIG:
{
opts.instant_exit = true;
std::ofstream out(opt.argument.c_str());
if (!out)
{
std::ostringstream str;
str << "Couldn't create " << opt.argument;
throw std::runtime_error(str.str());
}
else
{
// FIXME: implement me
m_ini.save(out);
}
}
break;
case OPTION_CONFIG:
{
std::ifstream in(opt.argument.c_str());
if (!in)
{
std::ostringstream str;
str << "Couldn't open " << opt.argument;
throw std::runtime_error(str.str());
}
else
{
INISchemaBuilder builder(m_ini);
INIParser parser(in, builder, opt.argument);
parser.run();
}
}
break;
case OPTION_TEST_RUMBLE:
opts.rumble = true;
break;
@ -359,11 +490,11 @@ CommandLineParser::parse_args(int argc, char** argv, Options* options)
break;
case OPTION_BUTTONMAP:
arg2vector(opt.argument, opts.button_map, &ButtonMapping::from_string);
arg2vector2(opt.argument, opts.button_map, &ButtonMapping::from_string);
break;
case OPTION_AXISMAP:
arg2vector(opt.argument, opts.axis_map, &AxisMapping::from_string);
arg2vector2(opt.argument, opts.axis_map, &AxisMapping::from_string);
break;
case OPTION_NAME:
@ -376,7 +507,7 @@ CommandLineParser::parse_args(int argc, char** argv, Options* options)
break;
case OPTION_UI_AXISMAP:
arg2apply(opt.argument, boost::bind(&set_ui_axis_map, opts.uinput_config.axis_map, _1));
arg2apply(opt.argument, boost::bind(&CommandLineParser::set_ui_axismap_from_string, this, _1));
break;
case OPTION_UI_BUTTONMAP:
@ -387,12 +518,12 @@ CommandLineParser::parse_args(int argc, char** argv, Options* options)
opts.uinput_config.dpad_as_button = true;
opts.deadzone = 4000;
opts.uinput_config.trigger_as_zaxis = true;
arg2vector("-y2=y2,-trigger=trigger", opts.axis_map, &AxisMapping::from_string);
arg2vector2("-y2=y2,-trigger=trigger", opts.axis_map, &AxisMapping::from_string);
// send events only every 20msec, lower values cause a jumpy pointer
arg2apply("x1=REL_X:15:20,y1=REL_Y:15:20,"
"y2=REL_WHEEL:5:100,x2=REL_HWHEEL:5:100,"
"trigger=REL_WHEEL:5:100",
boost::bind(&set_ui_axis_map, opts.uinput_config.axis_map, _1));
boost::bind(&CommandLineParser::set_ui_axismap_from_string, this, _1));
arg2apply("a=BTN_LEFT,b=BTN_RIGHT,x=BTN_MIDDLE,y=KEY_ENTER,rb=KEY_PAGEDOWN,lb=KEY_PAGEUP,"
"dl=KEY_LEFT,dr=KEY_RIGHT,du=KEY_UP,dd=KEY_DOWN,"
"start=KEY_FORWARD,back=KEY_BACK,guide=KEY_ESC,"
@ -409,11 +540,11 @@ CommandLineParser::parse_args(int argc, char** argv, Options* options)
break;
case OPTION_EVDEV_ABSMAP:
arg2apply(opt.argument, boost::bind(&set_evdev_absmap, boost::ref(opts.evdev_absmap), _1));
arg2apply(opt.argument, boost::bind(&::set_evdev_absmap, boost::ref(opts.evdev_absmap), _1));
break;
case OPTION_EVDEV_KEYMAP:
arg2apply(opt.argument, boost::bind(&set_evdev_keymap, boost::ref(opts.evdev_keymap), _1));
arg2apply(opt.argument, boost::bind(&::set_evdev_keymap, boost::ref(opts.evdev_keymap), _1));
break;
case OPTION_ID:
@ -469,19 +600,19 @@ CommandLineParser::parse_args(int argc, char** argv, Options* options)
break;
case OPTION_AUTOFIRE:
arg2vector(opt.argument, opts.autofire_map, &AutoFireMapping::from_string);
arg2vector2(opt.argument, opts.autofire_map, &AutoFireMapping::from_string);
break;
case OPTION_CALIBRARIOTION:
arg2vector(opt.argument, opts.calibration_map, &CalibrationMapping::from_string);
arg2vector2(opt.argument, opts.calibration_map, &CalibrationMapping::from_string);
break;
case OPTION_RELATIVE_AXIS:
arg2vector(opt.argument, opts.relative_axis_map, &RelativeAxisMapping::from_string);
arg2vector2(opt.argument, opts.relative_axis_map, &RelativeAxisMapping::from_string);
break;
case OPTION_AXIS_SENSITIVITY:
arg2vector(opt.argument, opts.axis_sensitivity_map, &AxisSensitivityMapping::from_string);
arg2vector2(opt.argument, opts.axis_sensitivity_map, &AxisSensitivityMapping::from_string);
break;
case OPTION_FOUR_WAY_RESTRICTOR:
@ -580,7 +711,7 @@ CommandLineParser::parse_args(int argc, char** argv, Options* options)
void
CommandLineParser::print_help() const
{
argp.print_help(std::cout);
m_argp.print_help(std::cout);
}
void
@ -619,54 +750,98 @@ CommandLineParser::print_version() const
}
void
CommandLineParser::create_ini_schema()
CommandLineParser::set_ui_axismap(const std::string& name, const std::string& value)
{
// INISchema ini;
XboxAxis axis = string2axis(name);
AxisEvent event = AxisEvent::from_string(value);
if (axis != XBOX_AXIS_UNKNOWN)
{
m_options->uinput_config.axis_map[axis] = event;
}
else
{
throw std::runtime_error("Couldn't convert string \"" + name + "=" + value + "\" to ui-axis-mapping");
}
}
// ini.section("general")
// ("verbose", &verbose)
// ("silent", &silent)
// ("quiet", &quiet)
// ("rumble", &rumble)
// ("led", &led)
// ("rumble_l", &rumble_l)
// ("rumble_r", &rumble_r)
// ("rumble_gain", &rumble_gain)
// ("controller-id", &controller_id)
// ("wireless-id", &wireless_id)
// ("instant-exit", &instant_exit)
// ("no-uinput", &no_uinput)
// ("detach-kernel-driver", &detach_kernel_driver);
void
CommandLineParser::set_ui_buttonmap(const std::string& name, const std::string& value)
{
std::string btn_str = name;
ButtonEvent event = ButtonEvent::from_string(value);
// char busid[4];
// char devid[4];
// int deadzone;
// int deadzone_trigger;
std::string::size_type j = btn_str.find('+');
if (j == std::string::npos)
{
XboxButton btn = string2btn(btn_str);
// int vendor_id;
// int product_id;
m_options->uinput_config.btn_map.bind(btn, event);
}
else
{
XboxButton shift = string2btn(btn_str.substr(0, j));
XboxButton btn = string2btn(btn_str.substr(j+1));
// bool square_axis;
// bool four_way_restrictor;
// int dpad_rotation;
// std::string evdev_device;
m_options->uinput_config.btn_map.bind(shift, btn, event);
}
}
// uInputCfg uinput_config;
//ini.section("ui-buttonmap", ui_buttonmap_cb);
//ini.section("ui-axismap", ui_buttonmap_cb);
void
CommandLineParser::set_axismap(const std::string& name, const std::string& value)
{
XboxAxis axis = string2axis(name);
AxisEvent event = AxisEvent::from_string(value);
if (axis != XBOX_AXIS_UNKNOWN)
{
m_options->uinput_config.axis_map[axis] = event;
}
else
{
throw std::runtime_error("Couldn't convert string \"" + value + "\" to ui-axis-mapping");
}
}
// std::vector<ButtonMapping> button_map;
//ini.section("buttonmap", buttonmap_cb);
void
CommandLineParser::set_buttonmap(const std::string& name, const std::string& value)
{
m_options->button_map.push_back(ButtonMapping::from_string(name, value));
}
// std::vector<AxisMapping> axis_map;
//ini.section("axismap", axismap_cb);
void
CommandLineParser::set_evdev_absmap(const std::string& name, const std::string& value)
{
m_options->evdev_absmap[str2abs(name)] = string2axis(value);
}
// std::vector<AutoFireMapping> autofire_map;
// std::vector<RelativeAxisMapping> relative_axis_map;
// std::vector<CalibrationMapping> calibration_map;
// std::vector<AxisSensitivityMapping> axis_sensitivity_map;
// std::map<int, XboxAxis> evdev_absmap;
// std::map<int, XboxButton> evdev_keymap;
void
CommandLineParser::set_evdev_keymap(const std::string& name, const std::string& value)
{
m_options->evdev_keymap[str2key(name)] = string2btn(value);
}
void
CommandLineParser::set_relative_axis(const std::string& name, const std::string& value)
{
m_options->relative_axis_map.push_back(RelativeAxisMapping::from_string(name, value));
}
void
CommandLineParser::set_autofire(const std::string& name, const std::string& value)
{
m_options->autofire_map.push_back(AutoFireMapping::from_string(name, value));
}
void
CommandLineParser::set_calibration(const std::string& name, const std::string& value)
{
m_options->calibration_map.push_back(CalibrationMapping::from_string(name, value));
}
void
CommandLineParser::set_axis_sensitivity(const std::string& name, const std::string& value)
{
m_options->axis_sensitivity_map.push_back(AxisSensitivityMapping::from_string(name, value));
}
/* EOF */

View file

@ -22,18 +22,21 @@
#include <vector>
#include <map>
#include "modifier.hpp"
#include "xboxmsg.hpp"
#include "uinput.hpp"
#include "arg_parser.hpp"
#include "ini_schema.hpp"
#include "modifier.hpp"
#include "uinput.hpp"
#include "xboxmsg.hpp"
class Xboxdrv;
class CommandLineParser
{
public:
ArgParser argp;
ArgParser m_argp;
INISchema m_ini;
Options* m_options;
public:
CommandLineParser();
@ -42,7 +45,29 @@ public:
void print_help() const;
void print_led_help() const;
void print_version() const;
void create_ini_schema();
void create_ini_schema(Options* opts);
private:
void set_ui_axismap_from_string(const std::string& str);
private:
void set_ui_axismap(const std::string& name, const std::string& value);
void set_ui_buttonmap(const std::string& name, const std::string& value);
void set_axismap(const std::string& name, const std::string& value);
void set_buttonmap(const std::string& name, const std::string& value);
void set_evdev_absmap(const std::string& name, const std::string& value);
void set_evdev_keymap(const std::string& name, const std::string& value);
void set_relative_axis(const std::string& name, const std::string& value);
void set_autofire(const std::string& name, const std::string& value);
void set_calibration(const std::string& name, const std::string& value);
void set_axis_sensitivity(const std::string& name, const std::string& value);
private:
void init_argp();
void init_ini(Options* opts);
};
extern Options* g_options;

View file

@ -19,6 +19,8 @@
#ifndef HEADER_XBOXDRV_INI_BUILDER_HPP
#define HEADER_XBOXDRV_INI_BUILDER_HPP
#include <string>
class INIBuilder
{
public:

View file

@ -18,10 +18,13 @@
#include "ini_schema.hpp"
#include <sstream>
class INIPairSchema
{
public:
virtual ~INIPairSchema() {}
virtual std::string str() const = 0;
};
class INIPairSchemaBool : public INIPairSchema
@ -46,8 +49,20 @@ public:
throw std::runtime_error("unable to convert '" + value + "' to bool");
}
}
};
std::string str() const
{
if (m_data)
{
return "true";
}
else
{
return "false";
}
}
};
class INIPairSchemaInt : public INIPairSchema
{
private:
@ -59,8 +74,15 @@ public:
{
*m_data = atoi(value.c_str());
}
};
std::string str() const
{
std::ostringstream str;
str << *m_data;
return str.str();
}
};
class INIPairSchemaFloat : public INIPairSchema
{
private:
@ -72,8 +94,15 @@ public:
{
*m_data = atoi(value.c_str());
}
};
std::string str() const
{
std::ostringstream str;
str << *m_data;
return str.str();
}
};
class INIPairSchemaString : public INIPairSchema
{
private:
@ -85,8 +114,14 @@ public:
{
*m_data = value;
}
};
std::string str() const
{
// FIXME: implement proper escaping
return *m_data;
}
};
class INIPairSchemaCallback : public INIPairSchema
{
private:
@ -99,8 +134,14 @@ public:
if (m_callback)
m_callback(value);
}
};
std::string str() const
{
// FIXME: implement me
return "<not implemented>";
}
};
INISchemaSection::INISchemaSection()
{
}
@ -163,17 +204,33 @@ INISchemaSection::operator()(const std::string& name, boost::function<void (cons
return *this;
}
void
INISchemaSection::save(std::ostream& out)
{
for(Schema::iterator i = m_schema.begin(); i != m_schema.end(); ++i)
{
out << i->first << " = " << i->second->str() << std::endl;
}
}
INISchema::INISchema() :
m_sections()
{
}
INISchema::~INISchema()
{
clear();
}
void
INISchema::clear()
{
for(Sections::iterator i = m_sections.begin(); i != m_sections.end(); ++i)
{
delete i->second;
}
m_sections.clear();
}
INISchemaSection&
@ -205,4 +262,16 @@ INISchema::get_section(const std::string& name)
}
}
void
INISchema::save(std::ostream& out)
{
for(Sections::iterator i = m_sections.begin(); i != m_sections.end(); ++i)
{
out << "[" << i->first << "]" << std::endl;
i->second->save(out);
out << std::endl;
}
out << "\n# EOF #" << std::endl;
}
/* EOF */

View file

@ -41,6 +41,8 @@ public:
INISchemaSection& operator()(const std::string& name, std::string* value);
INISchemaSection& operator()(const std::string& name, boost::function<void (const std::string&)> callback);
void save(std::ostream& out);
private:
INISchemaSection& add(const std::string& name, INIPairSchema* schema);
@ -59,12 +61,16 @@ public:
INISchema();
~INISchema();
void clear();
INISchemaSection& section(const std::string& name,
boost::function<void (const std::string&, const std::string&)> callback
= boost::function<void (const std::string&, const std::string&)>());
INISchemaSection* get_section(const std::string& name);
void save(std::ostream& out);
private:
INISchema(const INISchema&);
INISchema& operator=(const INISchema&);

View file

@ -0,0 +1,36 @@
/*
** Xbox360 USB Gamepad Userspace Driver
** Copyright (C) 2010 Ingo Ruhnke <grumbel@gmx.de>
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ini_schema_builder.hpp"
INISchemaBuilder::INISchemaBuilder(const INISchema& schema) :
m_schema(schema)
{
}
void
INISchemaBuilder::send_section(const std::string& section)
{
}
void
INISchemaBuilder::send_pair(const std::string& name, const std::string& value)
{
}
/* EOF */

View file

@ -0,0 +1,44 @@
/*
** Xbox360 USB Gamepad Userspace Driver
** Copyright (C) 2010 Ingo Ruhnke <grumbel@gmx.de>
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef HEADER_XBOXDRV_INI_SCHEMA_BUILDER_HPP
#define HEADER_XBOXDRV_INI_SCHEMA_BUILDER_HPP
#include "ini_builder.hpp"
class INISchema;
class INISchemaBuilder : public INIBuilder
{
private:
const INISchema& m_schema;
public:
INISchemaBuilder(const INISchema& schema);
void send_section(const std::string& section);
void send_pair(const std::string& name, const std::string& value);
private:
INISchemaBuilder(const INISchemaBuilder&);
INISchemaBuilder& operator=(const INISchemaBuilder&);
};
#endif
/* EOF */

View file

@ -71,56 +71,47 @@ void apply_axis_map(XboxGenericMsg& msg, const std::vector<AxisMapping>& lst)
msg = newmsg;
}
CalibrationMapping CalibrationMapping::from_string(const std::string& str)
CalibrationMapping CalibrationMapping::from_string(const std::string& lhs, const std::string& rhs)
{
std::string::size_type epos = str.find_first_of('=');
if (epos == std::string::npos)
{
throw std::runtime_error("Couldn't convert string \"" + str + "\" to CalibrationMapping");
}
else
{
CalibrationMapping mapping;
mapping.axis = string2axis(str.substr(0, epos));
mapping.min = -32768;
mapping.center = 0;
mapping.max = 32767;
CalibrationMapping mapping;
mapping.axis = string2axis(lhs);
mapping.min = -32768;
mapping.center = 0;
mapping.max = 32767;
boost::char_separator<char> sep(":", "", boost::keep_empty_tokens);
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
boost::char_separator<char> sep(":", "", boost::keep_empty_tokens);
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
std::string rhs = str.substr(epos+1);
tokenizer tokens(rhs, sep);
int j = 0;
for(tokenizer::iterator i = tokens.begin(); i != tokens.end(); ++i, ++j)
tokenizer tokens(rhs, sep);
int j = 0;
for(tokenizer::iterator i = tokens.begin(); i != tokens.end(); ++i, ++j)
{
//std::cout << "Token: '" << *i << "'" << std::endl;
if (!i->empty())
{
//std::cout << "Token: '" << *i << "'" << std::endl;
if (!i->empty())
try
{
try
{
if (j == 0)
mapping.min = boost::lexical_cast<int>(*i);
else if (j == 1)
mapping.center = boost::lexical_cast<int>(*i);
else if (j == 2)
mapping.max = boost::lexical_cast<int>(*i);
else
throw std::runtime_error("--calibration: to many arguments given, syntax is 'AXIS=MIN:CENTER:MAX': " + str);
}
catch(boost::bad_lexical_cast&)
{
throw std::runtime_error("--calibration: couldn't convert '" + *i + "' to int");
}
if (j == 0)
mapping.min = boost::lexical_cast<int>(*i);
else if (j == 1)
mapping.center = boost::lexical_cast<int>(*i);
else if (j == 2)
mapping.max = boost::lexical_cast<int>(*i);
else
throw std::runtime_error("--calibration: to many arguments given, syntax is 'AXIS=MIN:CENTER:MAX'");
}
catch(boost::bad_lexical_cast&)
{
throw std::runtime_error("--calibration: couldn't convert '" + *i + "' to int");
}
}
}
if (!(mapping.min <= mapping.center && mapping.center <= mapping.max))
throw std::runtime_error("Order wrong 'AXIS=MIN:CENTER:MAX': " + str);
if (!(mapping.min <= mapping.center && mapping.center <= mapping.max))
throw std::runtime_error("Order wrong 'AXIS=MIN:CENTER:MAX'");
return mapping;
}
return mapping;
}
static int clamp(int lhs, int rhs, int v)
@ -146,119 +137,77 @@ void apply_calibration_map(XboxGenericMsg& msg, const std::vector<CalibrationMap
}
ButtonMapping
ButtonMapping::from_string(const std::string& str)
ButtonMapping::from_string(const std::string& lhs, const std::string& rhs)
{
for(std::string::const_iterator i = str.begin(); i != str.end(); ++i)
{
if (*i == '=')
{
ButtonMapping mapping;
mapping.lhs = string2btn(std::string(str.begin(), i));
mapping.rhs = string2btn(std::string(i+1, str.end()));
return mapping;
}
}
throw std::runtime_error("Couldn't convert string \"" + str + "\" to button mapping");
ButtonMapping mapping;
mapping.lhs = string2btn(lhs);
mapping.rhs = string2btn(rhs);
return mapping;
}
AxisMapping
AxisMapping::from_string(const std::string& str)
AxisMapping::from_string(const std::string& lhs, const std::string& rhs)
{
for(std::string::const_iterator i = str.begin(); i != str.end(); ++i)
assert(!lhs.empty());
assert(!rhs.empty());
AxisMapping mapping;
if (lhs[0] == '-')
{
if (*i == '=')
{
AxisMapping mapping;
std::string lhs(str.begin(), i);
std::string rhs(i+1, str.end());
if (lhs.empty() || rhs.empty())
throw std::runtime_error("Couldn't convert string \"" + str + "\" to axis mapping");
if (lhs[0] == '-')
{
mapping.invert = true;
mapping.lhs = string2axis(lhs.substr(1));
}
else
{
mapping.invert = false;
mapping.lhs = string2axis(lhs);
}
mapping.rhs = string2axis(rhs);
if (mapping.lhs == XBOX_AXIS_UNKNOWN ||
mapping.rhs == XBOX_AXIS_UNKNOWN)
throw std::runtime_error("Couldn't convert string \"" + str + "\" to axis mapping");
return mapping;
}
}
throw std::runtime_error("Couldn't convert string \"" + str + "\" to axis mapping");
}
RelativeAxisMapping
RelativeAxisMapping::from_string(const std::string& str)
{
/* Format of str: A={SPEED} */
std::string::size_type i = str.find('=');
if (i == std::string::npos)
{
throw std::runtime_error("Couldn't convert string \"" + str + "\" to RelativeAxisMapping");
mapping.invert = true;
mapping.lhs = string2axis(lhs.substr(1));
}
else
{
RelativeAxisMapping mapping;
mapping.axis = string2axis(str.substr(0, i));
mapping.speed = boost::lexical_cast<int>(str.substr(i+1, str.size()-i));
// FIXME: insert some error checking here
return mapping;
mapping.invert = false;
mapping.lhs = string2axis(lhs);
}
mapping.rhs = string2axis(rhs);
if (mapping.lhs == XBOX_AXIS_UNKNOWN ||
mapping.rhs == XBOX_AXIS_UNKNOWN)
throw std::runtime_error("Couldn't convert string \"" + lhs + "=" + rhs + "\" to axis mapping");
return mapping;
}
RelativeAxisMapping
RelativeAxisMapping::from_string(const std::string& lhs, const std::string& rhs)
{
/* Format of str: A={SPEED} */
RelativeAxisMapping mapping;
mapping.axis = string2axis(lhs);
mapping.speed = boost::lexical_cast<int>(rhs);
// FIXME: insert some error checking here
return mapping;
}
AutoFireMapping
AutoFireMapping::from_string(const std::string& str)
AutoFireMapping::from_string(const std::string& lhs, const std::string& rhs)
{
/* Format of str: A={ON-DELAY}[:{OFF-DELAY}]
Examples: A=10 or A=10:50
if OFF-DELAY == nil then ON-DELAY = OFF-DELAY
*/
std::string::size_type i = str.find_first_of('=');
if (i == std::string::npos)
{
throw std::runtime_error("Couldn't convert string \"" + str + "\" to AutoFireMapping");
}
else
{
AutoFireMapping mapping;
mapping.button = string2btn(str.substr(0, i));
mapping.frequency = boost::lexical_cast<int>(str.substr(i+1, str.size()-i).c_str());
return mapping;
}
AutoFireMapping mapping;
mapping.button = string2btn(lhs);
mapping.frequency = boost::lexical_cast<int>(rhs);
return mapping;
}
AxisSensitivityMapping
AxisSensitivityMapping::from_string(const std::string& str)
AxisSensitivityMapping::from_string(const std::string& lhs, const std::string& rhs)
{
/*
Format of str: X1=SENSITIVITY
Example: X1=2.0
*/
std::string::size_type i = str.find_first_of('=');
if (i == std::string::npos)
{
throw std::runtime_error("Couldn't convert string \"" + str + "\" to AxisSensitivityMapping");
}
else
{
AxisSensitivityMapping mapping;
mapping.axis = string2axis(str.substr(0, i));
mapping.sensitivity = boost::lexical_cast<float>(str.substr(i+1, str.size()-i).c_str());
return mapping;
}
AxisSensitivityMapping mapping;
mapping.axis = string2axis(lhs);
mapping.sensitivity = boost::lexical_cast<float>(rhs);
return mapping;
}
void squarify_axis_(int16_t& x_inout, int16_t& y_inout)

View file

@ -27,14 +27,14 @@
class Options;
struct ButtonMapping {
static ButtonMapping from_string(const std::string& str);
static ButtonMapping from_string(const std::string& lhs, const std::string& rhs);
XboxButton lhs;
XboxButton rhs;
};
struct AxisMapping {
static AxisMapping from_string(const std::string& str);
static AxisMapping from_string(const std::string& lhs, const std::string& rhs);
XboxAxis lhs;
XboxAxis rhs;
@ -42,21 +42,21 @@ struct AxisMapping {
};
struct AutoFireMapping {
static AutoFireMapping from_string(const std::string&);
static AutoFireMapping from_string(const std::string& lhs, const std::string& rhs);
XboxButton button;
int frequency;
};
struct RelativeAxisMapping {
static RelativeAxisMapping from_string(const std::string&);
static RelativeAxisMapping from_string(const std::string& lhs, const std::string& rhs);
XboxAxis axis;
int speed;
};
struct CalibrationMapping {
static CalibrationMapping from_string(const std::string&);
static CalibrationMapping from_string(const std::string& lhs, const std::string& rhs);
XboxAxis axis;
int min;
@ -65,7 +65,7 @@ struct CalibrationMapping {
};
struct AxisSensitivityMapping {
static AxisSensitivityMapping from_string(const std::string&);
static AxisSensitivityMapping from_string(const std::string& lhs, const std::string& rhs);
XboxAxis axis;
float sensitivity;

View file

@ -29,8 +29,8 @@ class uInputCfg
public:
std::string device_name;
bool trigger_as_button;
bool dpad_as_button;
bool trigger_as_zaxis;
bool dpad_as_button;
bool dpad_only;
bool force_feedback;
bool extra_devices;