├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── STEAM.md ├── devinput-parser.cpp ├── devinput-parser.h ├── devinput.lem ├── event-codes.cpp ├── event-codes.h ├── event-codes.inc ├── examples ├── xbox.ini └── xbox2.ini ├── inifile.cpp ├── inifile.h ├── inputdev.cpp ├── inputdev.h ├── inputmap.cpp ├── inputsteam.cpp ├── inputsteam.h ├── lemon ├── lemon.c └── lempar.c ├── meson.build ├── outputdev.cpp ├── outputdev.h ├── quaternion.h ├── steam ├── LICENSE ├── fd.h ├── steamcontroller.cpp ├── steamcontroller.h └── udev-wrapper.h └── util └── unique_handle.h /.gitattributes: -------------------------------------------------------------------------------- 1 | lemon/* linguist-vendored 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | debug 2 | release 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # inputmap 2 | Creates virtual input devices from real ones, SteamController support included. 3 | 4 | This program lets you create a virtual input device, by using the Linux `uinput` module, and map to it 5 | any number of buttons or axes from real devices. Currently, it is able to use raw input devices and SteamControllers. 6 | 7 | The main motivation for this program is to be able to use the SteamController as a normal gamepad, but you can find many other uses for it. 8 | 9 | If you are looking for information about the SteamController protocol, then read the [STEAM.md](STEAM.md) file. 10 | 11 | ## License 12 | 13 | This program is licensed under the GPLv3 or later, except: 14 | 15 | * Directory `steam`, the implementation of the SteamController decoder, that is under the LGPLv3 or later. 16 | * Directory `lemon`, the Lemon parser generator, by D. Richard Hipp, that is Public Domain. 17 | 18 | ## How to compile 19 | 20 | This program uses the meson/ninja build system. To build, install meson and ninja from your Linux distribution, and run: 21 | 22 | $ meson --buildtype release build 23 | $ ninja -C build install 24 | 25 | ## How it works 26 | 27 | To create a virtual device you must write a file describing the real input devices and how they map to the virtual device you want to create. This file uses the INI file syntax. 28 | Then you just run: 29 | 30 | $ inputmap configuration.ini 31 | 32 | And you are ready to go. You may need to run the program as root, depending on your system configuration. 33 | 34 | ## Configuration file syntax 35 | 36 | The INI configuration file is quite simple. You can only have two types of sections: 37 | 38 | * `[input]`: defines a standard input physical device. 39 | * `[steam]`: defines a SteamController input physical device. 40 | * `[output]`: defines an output virtual device. 41 | 42 | You can write as many sections of any of these as you want. 43 | 44 | ### `[input]` section. 45 | 46 | There are several ways to describe the device referred to by this section: 47 | 48 | * `dev`: The full name of the input device, such as `/dev/input/event1`. Since the device name is not stable across reboots, you should try other approach first. 49 | * `by-id`: A shortcut to refer to a device from `/dev/input/by-id`. This one should be stable across reboots. 50 | * `by-path`: A shortcut to refer to a device from `/dev/input/by-path`. This identifies the port where the device is connected plut the device itself. 51 | * `by-name`: Identifies the device by its name. 52 | * `by-uniq`: Identifies the device by its unique name, that is, its serial number, if available. 53 | * `usb`: A tuple of two hexadecimal values, separated by `:` that identifies the USB device, such as `1234:5678`. 54 | * `pci`, `i8042`, `bluetooth`, ...: Just like `usb`, but for this other bus. Only one bus can be specified. 55 | 56 | You can see the available devices and its properties by using the `-v` option. 57 | 58 | These values are mandatory: 59 | 60 | * `name`: the name to refer to this device from the rest of the file. A single unique word, made of letters numbers and underscore. 61 | 62 | These values are optional: 63 | 64 | * `grab`: a boolean value (`Y` / `N`), defaults to `N`. If `Y` then the device is _grabbed_, that is it is prevented from sending input events to the rest of the system. 65 | 66 | ### `[steam]` section. 67 | 68 | This section describes a SteamController device. The `name` value is mandatory, just like in `[input]`. Additionally, there are these possible values: 69 | 70 | * `serial`: Only a SteamController with this serial number will be used for this input device. Useful if you have several of them. 71 | * `mouse`: a boolean value (`Y` / `N`), defaults to `N`. If `N` then the builtin mouse emulation of the controller will be disabled. 72 | * `auto_haptic`: a character string `L`, `R` or `LR`, defaults to empty. If it has a `L` then it will enable automatic haptic feedback on the left pad. If it has a `R` then it will do the same on the right pad. 73 | 74 | ### `[output]` section. 75 | 76 | In this section you will define the virtual device. First you have a few optional values to describe the device: 77 | 78 | * `name`: The name of the virtual device, defaults to `InputMap`. 79 | * `phys`: The name of the _phys_ parameter of the virtual device, defaults to empty. 80 | * `bus`: The bus that the virtual device reports as using, it can be `VIRTUAL`, `USB`, `BLUETOOTH` or `PCI`, defaults to `VIRTUAL`. 81 | * `vendor`: An hexadecimal number to be reported as VendorId, defaults to 0. Useful to emulate well known devices. 82 | * `product`: An hexadecimal number to be reported as ProductId, defaults to 0. 83 | * `version`: The version of the device, mostly useless. Defaults to 1. 84 | 85 | Additionally, you map all the buttons and axes of the virtual device and how the physical devices map to them. 86 | 87 | #### Maps in the `[output]` section 88 | 89 | To create a virtual button, you just define a value in the `[output]` section with the name of that button. 90 | You can see the available button names in `/usr/include/linux/input-event-codes.h. 91 | 92 | The value itself describes how the value of that button is obtained. The easiest way is to write the name of an input device, a period, and the name of the physical button. 93 | 94 | For example, to map the keys 1 and 2 to the left and right mouse buttons: 95 | 96 | [input] 97 | by-id=usb-USB_Keyboard-event-kbd 98 | name=keyb 99 | 100 | [output] 101 | BTN_LEFT=keyb.KEY_1 102 | BTN_RIGHT=keyb.KEY_2 103 | 104 | To create a virtual axis, either relative or absolute, you do the same as with the buttons: 105 | 106 | [input] 107 | by-id=usb-USB_Joystick-event-joystick 108 | name=joy 109 | 110 | [output] 111 | ABS_X=joy.ABS_Z 112 | ABS_Y=joy.ABS_RZ 113 | 114 | You can also build a virtual input from physical inputs of a different kind. 115 | For example, to create a virtual absolute axis from two buttons (useful for the _hat_ axis) you write the two buttons within parentheses separated by comma: 116 | 117 | [output] 118 | ABS_HAT0X=(keyb.KEY_A,keyb.KEY_D) 119 | ABS_HAT0Y=(keyb.KEY_S,keyb.KEY_W) 120 | 121 | ## Systemd 122 | You can start inputmap from udev when the device is connected. 123 | 124 | ### inputmap config - use a macro to uniquely identify devices. 125 | [steam] 126 | name=Steam 127 | mouse=N 128 | serial={SERIAL} 129 | 130 | ### udev rule - pass identifer to systemd unit 131 | # /etc/udev/rules.d/90-inputmap.rules 132 | SUBSYSTEM=="input", ATTRS{name}=="*Steam Controller", TAG+="systemd", ENV{SYSTEMD_WANTS}="inputmap@%s{uniq}.service" 133 | 134 | ### systemd unit - pass systemd instance as macro 135 | # /etc/systemd/system/inputmap@.service 136 | [Unit] 137 | Description=%i service for inputmap 138 | 139 | [Service] 140 | ExecStart=/usr/bin/inputmap -m SERIAL=%i /etc/inputmap/xbox.ini 141 | -------------------------------------------------------------------------------- /STEAM.md: -------------------------------------------------------------------------------- 1 | # The SteamController 2 | 3 | The SteamController is a USB HID device, but unfortunately the Linux kernel does not recognize it as a proper input device (yet). 4 | This is because, although it is a HID device, it does not send the usual HID codes. In this document I will document how I think this device works. 5 | 6 | ## Connection 7 | 8 | The SteamController can be connected to the PC, either by using a USB cable or using the wireless adaptor, provided by the manufacturer. 9 | 10 | When the device is connected with a cable it shows as device `28de:1102`, which contains 3 USB interfaces: 11 | 12 | 1. A virtual keyboard, to send emulated key events, for keys `ENTER`, `ESC`, etc. 13 | 2. A virtual mouse, by default the right pad sends mouse events through this interface. 14 | 3. A vendor defined interface that implements the vendor defined protocol. From now on I will call this interface and protocol _Steam. 15 | 16 | When the wireless adaptor is connected it shows as device `28de:1142`, which contains 5 USB interfaces: 17 | 18 | * A virtual keyboard, like the wired one (no virtual mouse, sorry). 19 | * 4 Steam interfaces, to be able to connect up to 4 controller wirelessly. 20 | 21 | ## The hidraw device 22 | 23 | The Steam interface does implement the HID protocol, but it does not define any standard input, so the Linux kernel is not able to create an input device for it. 24 | It creates a hidraw device, instead (`/dev/hidraw*`). 25 | 26 | The HID descriptor for this device is as follows: 27 | 28 | 06 00 FF 09 01 A1 01 15 00 26 FF 00 75 08 95 40 09 01 81 02 95 40 09 01 91 02 95 40 09 01 B1 02 C0 29 | 30 | That, if I read the HID tables correctly, is decoded as: 31 | 32 | 1: USAGE_PAGE = 00 FF 33 | 2: USAGE = 01 34 | 0: BEGIN_COLLECTION = 01 35 | 1: LOGICAL_MIN = 00 36 | 1: LOGICAL_MAX = FF 00 37 | 1: REPORT_SIZE = 08 38 | 1: REPORT_COUNT = 40 39 | 2: USAGE = 01 40 | 0: INPUT = 02 41 | 1: REPORT_COUNT = 40 42 | 2: USAGE = 01 43 | 0: OUTPUT = 02 44 | 1: REPORT_COUNT = 40 45 | 2: USAGE = 01 46 | 0: FEATURE = 02 47 | 0: END_COLLECTION 48 | 49 | That basically means that it generates input reports of 64 vendor defined bytes and that it accepts output reports of 64 vendor defined bytes. 50 | 51 | It also defines a _feature_ of 64 bytes. That is a kind of special command that can be sent or received offside the usual events. 52 | 53 | What this means is that if you read the Steam `/dev/hidraw*` device you will get blocks of 64 bytes each, with vendor specific meaning. 54 | 55 | ## The input report 56 | 57 | Each block of data read from the Steam hidraw device contains 64 bytes of data. I have managed to make sense of most of them. 58 | 59 | In the following tables I will describe what I know. 60 | 61 | The basic structure of a data frame is as follows, all multibyte values are little-endian: 62 | 63 | | Byte index | Name | 64 | | -------------: |-------------| 65 | | 0-1 | Version | 66 | | 2 | Type of Message | 67 | | 3 | Size \ 68 | | 4-7 | Sequence Number | 69 | 70 | These values are: 71 | 72 | * Version: Probably the version of the protocol, currently it is always 0x0001. 73 | * Type of Message (ToM): I have only seen 3 types of message: 0x01=input data; 0x03=wireless connect/disconnect; 0x04=battery status. 74 | * Size: The number of useful bytes of this message, counting from the next one. It depends on the type of message, if ToM=0x01, Size=0x3C; if ToM=0x03, Size=0x01; if ToM=0x04, Size=0x0B. 75 | * Sequence Number: a 32-bit number that is incremented for each message. It is useful to know if you missed any message, for example. It is not available when ToM=0x03 (wireless connect/disconnect). 76 | 77 | The rest of the bytes depend on the value of ToM. 78 | 79 | ### The _wireless connect/disconnect_ report 80 | 81 | When ToM equals 0x03, that means that a wireless device has been connected/disconnected to this Steam interface. The wired device does not generate this report, it just appears or vanishes directly, but the 4 wireless devices exist whenever the wireless adaptor is connected. 82 | 83 | The data bytes of this report are: 84 | 85 | | Byte index | Name | 86 | | -------------: |-------------| 87 | | 4 | Connection | 88 | 89 | And the description: 90 | 91 | * Connection: 0x01 means device disconnected, 0x02 means device connected. 92 | 93 | ### The _battery status_ report 94 | 95 | When ToM equals 0x04, it is a status report from the device. It works as a ping, reminding you that the device still works, and as a battery status. 96 | 97 | The data bytes of this report are: 98 | 99 | | Byte index | Name | 100 | | -------------: |-------------| 101 | | 8-11 | Unknown, always 0 | 102 | | 12-13 | Volts | 103 | | 14 | Battery | 104 | 105 | And the description: 106 | 107 | * Volts: The voltage from the batteries, in millivolts. If it is wired, it'll measure the USB voltage (around 5000), if it is wireless, it is the voltage of the batteries. 108 | * Battery: The battery level as a percentage, from 0 to 100 (0x64). A wired device will always have 100% battery. 109 | 110 | ### The _input_ report 111 | 112 | When ToM equals 0x01 it is the most interesting one, it contains the input report with the status of all the buttons and axes of the device. If the device is connected wirelessly, then some of the fields are always 0, these are marked in the table with an asterisk (\*). 113 | 114 | The data fields in this report are: 115 | 116 | | Byte index | Name | 117 | | -------------: |-------------| 118 | | 8-10 | Buttons | 119 | | 11 | Left trigger value | 120 | | 12 | Right trigger value | 121 | | 13-15 | Unknown, always 0, probably for future buttons so that you can read 8-15 as a 64-bit value | 122 | | 16-17 | X coord. | 123 | | 18-19 | Y coord. | 124 | | 20-21 | Right pad X coord. | 125 | | 22-23 | Right pad Y coord. | 126 | | 24-25 | \* Left trigger value (16-bit) | 127 | | 26-27 | \* Right trigger value (16-bit) | 128 | | 28-29 | \* Accelerometer X value | 129 | | 30-31 | \* Accelerometer Y value | 130 | | 32-33 | \* Accelerometer Z value | 131 | | 34-35 | Gyroscopic X value | 132 | | 36-37 | Gyroscopic Y value | 133 | | 38-39 | Gyroscopic Z value | 134 | | 40-41 | Quaternion W value | 135 | | 42-43 | Quaternion X value | 136 | | 44-45 | Quaternion Y value | 137 | | 46-47 | Quaternion Z value | 138 | | 48-49 | Unknown, always 0 | 139 | | 50-51 | \* Uncalibrated left trigger | 140 | | 52-53 | \* Uncalibrated right trigger | 141 | | 54-55 | \* Uncalibrated X joystick | 142 | | 56-57 | \* Uncalibrated Y joystick | 143 | | 58-59 | \* Left pad X coord. | 144 | | 60-61 | \* Left pad Y coord. | 145 | | 62-63 | \* Voltage | 146 | 147 | 148 | Each bit from the 24 bit Buttons value is a button, 0 is released, 1 is pressed: 149 | 150 | * Bit 0: Right trigger fully pressed. 151 | * Bit 1: Left trigger fully pressed. 152 | * Bit 2: Right bumper (the button above the trigger). 153 | * Bit 3: Left bumber. 154 | * Bit 4: Button Y. 155 | * Bit 5: Button B. 156 | * Bit 6: Button X. 157 | * Bit 7: Button A. 158 | * Bit 8: North, the left pad has been clicked in the upper side. 159 | * Bit 9: East, the left pad has been clicked in the right side. 160 | * Bit 10: West, the left pad has been clicked in the left side. 161 | * Bit 11: South, the left pad has been clicked in the bottom side. 162 | * Bit 12: Menu, the button with a left arrow next to the Steam button. 163 | * Bit 13: Steam button. 164 | * Bit 14: Escape, the button with a right arrow next to the Steam button. 165 | * Bit 15: Left back pedal. 166 | * Bit 16: Right back pedal. 167 | * Bit 17: Left pad clicked. 168 | * Bit 18: Right pad clicked. 169 | * Bit 19: Left pad touched. 170 | * Bit 20: Right pad touched. 171 | * Bit 21: Unknown. 172 | * Bit 22: Joystick pressed. 173 | * Bit 23: LPadAndJoy. 174 | 175 | The values _X coord._ and _Y coord._ deserve a special explanation. Read as-is, they represent the position of the joystick or a touch in the left pad, whatever the user is doing. The idea is that these two controllers should be mostly equivalent. If you want to know which one is being used, just check for the _Left pad touched_ button: if it is on, then it is a left pad event, if not it is a joystick event. 176 | 177 | But what if you want to manage input from the left pad and the joystick at the same time? Well, then you need to take into account the _Left pad touched_ and the _LPadAndJoy_ buttons: 178 | 179 | | Left pad touched | LPadAndJoy | Meaning of X/Y coord. | Is left pad touched | Is joystick moved | 180 | | ---------------- | ----------- | --------------------- | ------------------- | ----------------- | 181 | | 0 | 0 | Joystick position | No | Maybe, if <> 0 | 182 | | 1 | 0 | Left pad coord. | Yes | No | 183 | | 0 | 1 | Joystick position | Yes | Yes | 184 | | 1 | 1 | Left pad position | Yes | Yes | 185 | 186 | That is, when both controls are used at the same time, you will get alternating events with the two last rows of this table. You can get the left pad coordinates unconditionally from the _Left pad X/Y coord_ values, but those unfortunately are not available with the wireless device. 187 | 188 | 189 | 190 | ## Feature reports 191 | 192 | The Steam hidraw device also accepts commands in the form of a feature report. It is sent by using a `ioctl()` call with the `HIDIOCSFEATURE` value. Some commands have a reply, that can be read with the `HIDIOCGFEATURE` `ioctl()`. See the source code for details of how they are sent and received. 193 | 194 | All the commands are 64 bytes in length, although in practice most commands use only a few of them. The general structure of the command is: 195 | 196 | | Byte index | Name | 197 | | -------------: |-------------| 198 | | 0 | Command code | 199 | | 1 | Length of the command data | 200 | | 2- | Data of the command | 201 | 202 | The following commands are currently known: 203 | 204 | * 0x85: Enables the emulation of the virtual keyboard when the buttons are pressed. 205 | * 0x81: Disables the emulation of the virtual keyboard. 206 | * 0x8E: Resets the device to the default emulation values. 207 | * 0xAE 0x15 0x01: Gets the serial number. The reply will contain the serial number in positions 3-13. 208 | * 0xAE 0x15 0x00: Gets the board information. The reply will contain the board number in positions 3-13. 209 | * 0x83: Version information. It will return 30 bytes, or 6 structures of 5 bytes each. The first byte of the structure identifies the value, the other 4 are the value itself. These are the known values: 210 | - 0x00: Unknown, maybe firmware version. 211 | - 0x01: Product id. The constant 0x1102 is the Steam Controller. 212 | - 0x02: Capabilities, always 0x03. 213 | - 0x04: firmware build time. 214 | - 0x05: some unknown time. 215 | - 0x0A: bootloader build time. 216 | * 0xBA: returns some unknown information. 217 | * 0x87 0x03 R Blow Bhigh: Writes 16-bit value B into register R. Several writes can be combined into a single command, such as `0x87 0x06 R1 XL XH R2 YL YH`. The known registers are: 218 | - 0x30: Accelerometer: 0x00=disabled, 0x14=enabled. 219 | - 0x08: Write 0x07 to disable mouse keys emulation, 0x00 to enable. 220 | - 0x07: Write 0x07 to disable mouse cursor emulation, 0x00 to enable. 221 | - 0x18: Write 0x00 to remove the margin of the right pad. By default touching the rim of the right pad does not generate events. 222 | - 0x2d: Led intensity, from 0x00 to 0x64. 223 | * 0x8F: Send haptic feedback. See below. 224 | 225 | 226 | ## Haptic feedback 227 | 228 | The little motors in the device are activated with the command 0x8F, as described in the previous section. This command takes 7 or 8 bytes of data, so the command is as follows: 229 | 230 | | Byte index | Name | 231 | | -------------: |-------------| 232 | | 0 | 0x8F (Command) | 233 | | 1 | 0x07 or 0x08 (Length) | 234 | | 2 | Side | 235 | | 3-4 | Time On | 236 | | 5-6 | Time Off | 237 | | 7-8 | Count | 238 | | 9 | Unknown | 239 | 240 | The Side value is: 241 | 242 | * 0: right motor. 243 | * 1: left motor. 244 | 245 | The rest of the values describe the shape of the signal that is sent to the motor. As far as I know it sends a square signal to the motor that makes it vibrate. For each cicle of the signal you can control the duration of the ON and the OFF sections. And also the number of cycles in the signal. When all these cycles has been sent, the motors stops by itself. If you send another signal to the same motor, the previous one is forgotten. The Time On/Off values are measured in microseconds. 246 | 247 | So if you want to send a signal of _freq_ Hz with a duty cycle of _duty_% for _duration_ seconds, you will send, you could use the following code to compute the actual parameters of the command: 248 | 249 | int period = 1000000 / freq; //in us 250 | int time_on = period * duty / 100; 251 | int time_off = period - time_on; 252 | int count = duration / period; 253 | 254 | -------------------------------------------------------------------------------- /devinput-parser.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017, Rodrigo Rivas Costa 4 | 5 | This file is part of inputmap. 6 | 7 | inputmap is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | inputmap is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with inputmap. If not, see . 19 | 20 | */ 21 | 22 | #include 23 | #include "devinput-parser.h" 24 | #include "devinput.h" 25 | #include "quaternion.h" 26 | 27 | value_t ValueRef::get_value() 28 | { 29 | auto dev = m_device.lock(); 30 | if (!dev) 31 | return 0; 32 | return dev->get_value(m_value_id); 33 | } 34 | 35 | value_t ValueCond::get_value() 36 | { 37 | value_t c = m_cond->get_value(); 38 | return (c ? m_true : m_false)->get_value(); 39 | } 40 | bool ValueCond::is_constant() const 41 | { 42 | if (!m_cond->is_constant()) 43 | return false; 44 | value_t c = m_cond->get_value(); 45 | return (c ? m_true : m_false)->is_constant(); 46 | } 47 | 48 | value_t ValueOper::get_value() 49 | { 50 | switch (m_oper) 51 | { 52 | case InputToken_PLUS: 53 | return m_left->get_value() + m_right->get_value(); 54 | case InputToken_MINUS: 55 | return m_left->get_value() - m_right->get_value(); 56 | case InputToken_LT: 57 | return m_left->get_value() < m_right->get_value()? 1 : 0; 58 | case InputToken_GT: 59 | return m_left->get_value() > m_right->get_value()? 1 : 0; 60 | case InputToken_AND: 61 | { 62 | value_t a = m_left->get_value(); 63 | return a ? m_right->get_value() : 0; 64 | }; 65 | case InputToken_OR: 66 | { 67 | value_t a = m_left->get_value(); 68 | return a ? a : m_right->get_value(); 69 | } 70 | case InputToken_MULT: 71 | return m_left->get_value() * m_right->get_value(); 72 | case InputToken_DIV: 73 | { 74 | value_t r = m_right->get_value(); 75 | if (r != 0) 76 | return m_left->get_value() / r; 77 | else 78 | return 0; 79 | } 80 | default: 81 | return 0; 82 | } 83 | } 84 | bool ValueOper::is_constant() const 85 | { 86 | if (!m_left->is_constant()) 87 | return false; 88 | if (m_right->is_constant()) 89 | return true; 90 | 91 | value_t a = m_left->get_value(); 92 | switch (m_oper) 93 | { 94 | case InputToken_AND: 95 | return !a; 96 | case InputToken_OR: 97 | return a; 98 | default: 99 | return false; 100 | } 101 | } 102 | 103 | value_t ValueUnary::get_value() 104 | { 105 | switch (m_oper) 106 | { 107 | case InputToken_MINUS: 108 | return -m_expr->get_value(); 109 | case InputToken_NOT: 110 | return !m_expr->get_value(); 111 | default: 112 | return 0; 113 | } 114 | } 115 | 116 | ////////////////////////// 117 | // Functions 118 | 119 | class ValueFunc1 : public ValueExpr 120 | { 121 | public: 122 | ValueFunc1(value_t (*f)(value_t), std::unique_ptr &&e1) 123 | :m_fun(f), m_e1(std::move(e1)) 124 | { 125 | } 126 | value_t get_value() override 127 | { 128 | return m_fun(m_e1->get_value()); 129 | } 130 | private: 131 | value_t (*m_fun)(value_t); 132 | std::unique_ptr m_e1; 133 | }; 134 | 135 | class ValueFunc2 : public ValueExpr 136 | { 137 | public: 138 | ValueFunc2(value_t (*f)(value_t,value_t), std::unique_ptr &&e1, std::unique_ptr &&e2) 139 | :m_fun(f), m_e1(std::move(e1)), m_e2(std::move(e2)) 140 | { 141 | } 142 | value_t get_value() override 143 | { 144 | return m_fun(m_e1->get_value(), m_e2->get_value()); 145 | } 146 | private: 147 | value_t (*m_fun)(value_t,value_t); 148 | std::unique_ptr m_e1, m_e2; 149 | }; 150 | 151 | class ValueFunc3 : public ValueExpr 152 | { 153 | public: 154 | ValueFunc3(value_t (*f)(value_t,value_t,value_t), std::unique_ptr &&e1, std::unique_ptr &&e2, std::unique_ptr &&e3) 155 | :m_fun(f), m_e1(std::move(e1)), m_e2(std::move(e2)), m_e3(std::move(e3)) 156 | { 157 | } 158 | value_t get_value() override 159 | { 160 | return m_fun(m_e1->get_value(), m_e2->get_value(), m_e3->get_value()); 161 | } 162 | private: 163 | value_t (*m_fun)(value_t,value_t,value_t); 164 | std::unique_ptr m_e1, m_e2, m_e3; 165 | }; 166 | 167 | ValueExpr *create_func_ex(value_t(*f)(value_t), std::vector> &&exprs) 168 | { 169 | if (exprs.size() != 1) 170 | throw std::runtime_error("wrong number of arguments in function"); 171 | return new ValueFunc1(f, std::move(exprs[0])); 172 | } 173 | ValueExpr *create_func_ex(value_t(*f)(value_t,value_t), std::vector> &&exprs) 174 | { 175 | if (exprs.size() != 2) 176 | throw std::runtime_error("wrong number of arguments in function"); 177 | return new ValueFunc2(f, std::move(exprs[0]), std::move(exprs[1])); 178 | } 179 | ValueExpr *create_func_ex(value_t(*f)(value_t,value_t,value_t), std::vector> &&exprs) 180 | { 181 | if (exprs.size() != 3) 182 | throw std::runtime_error("wrong number of arguments in function"); 183 | return new ValueFunc3(f, std::move(exprs[0]), std::move(exprs[1]), std::move(exprs[2])); 184 | } 185 | 186 | value_t func_between(value_t a, value_t b, value_t c) 187 | { 188 | if (b < c) 189 | return b <= a && a < c; 190 | else 191 | return c <= a && a < b; 192 | } 193 | 194 | value_t func_between_angle(value_t angle, value_t from, value_t to) 195 | { 196 | while (to < from) 197 | to += 2 * M_PI; 198 | while (angle < from) 199 | angle += 2 * M_PI; 200 | bool res = angle < to; 201 | return res; 202 | } 203 | 204 | value_t func_bool(value_t a) 205 | { 206 | return a != 0; 207 | } 208 | 209 | class ValueMouse : public ValueExpr 210 | { 211 | public: 212 | ValueMouse(std::unique_ptr touch, std::unique_ptr x) 213 | :m_touch(std::move(touch)), m_x(std::move(x)), m_touching(false), m_old(0) 214 | { 215 | } 216 | value_t get_value() override 217 | { 218 | value_t touch = m_touch->get_value(); 219 | if (!touch) 220 | { 221 | m_touching = false; 222 | return 0; 223 | } 224 | value_t x = m_x->get_value(); 225 | value_t old = m_old; 226 | m_old = x; 227 | if (!m_touching) 228 | { 229 | m_touching = true; 230 | return 0; 231 | } 232 | return x - old; 233 | } 234 | private: 235 | std::unique_ptr m_touch, m_x, m_fuzz; 236 | bool m_touching; 237 | value_t m_old; 238 | }; 239 | 240 | class ValueStep : public ValueExpr 241 | { 242 | public: 243 | ValueStep(std::unique_ptr x, std::unique_ptr step) 244 | :m_x(std::move(x)), m_step(std::move(step)), m_old(0) 245 | { 246 | } 247 | value_t get_value() override 248 | { 249 | value_t x = m_x->get_value(); 250 | value_t step = m_step->get_value(); 251 | 252 | m_old += x; 253 | value_t m = fmod(m_old, step); 254 | value_t res = (m_old - m) / step; 255 | 256 | m_old = m; 257 | return res; 258 | } 259 | private: 260 | std::unique_ptr m_x, m_step; 261 | value_t m_old; 262 | }; 263 | 264 | class ValueDefuzz : public ValueExpr 265 | { 266 | public: 267 | ValueDefuzz(std::unique_ptr x, std::unique_ptr fuzz) 268 | :m_x(std::move(x)), m_fuzz(std::move(fuzz)), m_old(0) 269 | { 270 | } 271 | value_t get_value() override 272 | { 273 | value_t x = m_x->get_value(); 274 | value_t old = m_old; 275 | value_t fuzz = m_fuzz->get_value(); 276 | if (fuzz && x != 0) 277 | { 278 | if (old - fuzz / 2 < x && x < old + fuzz / 2) 279 | x = old; 280 | 281 | if (old - fuzz < x && x < old + fuzz) 282 | x = (old * 3 + x) / 4; 283 | 284 | if (old - fuzz * 2 < x && x < old + fuzz * 2) 285 | x = (old + x) / 2; 286 | } 287 | m_old = x; 288 | return x; 289 | } 290 | private: 291 | std::unique_ptr m_x, m_fuzz; 292 | bool m_touching; 293 | value_t m_old; 294 | }; 295 | 296 | class ValueTurbo : public ValueExpr 297 | { 298 | public: 299 | ValueTurbo(std::unique_ptr x) 300 | :m_x(std::move(x)), m_clicked(false) 301 | { 302 | } 303 | value_t get_value() override 304 | { 305 | value_t x = m_x->get_value(); 306 | if (x) 307 | m_clicked = !m_clicked; 308 | else 309 | m_clicked = false; 310 | return m_clicked? 1 : 0; 311 | } 312 | private: 313 | std::unique_ptr m_x; 314 | bool m_clicked; 315 | }; 316 | 317 | class ValueToggle : public ValueExpr 318 | { 319 | public: 320 | ValueToggle(std::unique_ptr x, int states) 321 | :m_x(std::move(x)), m_prev(false), m_current(0), m_states(states) 322 | { 323 | } 324 | value_t get_value() override 325 | { 326 | bool x = m_x->get_value() != 0; 327 | if (!m_prev && x) //edge on 328 | m_current = (m_current + 1) % m_states; 329 | m_prev = x; 330 | return m_current; 331 | } 332 | private: 333 | std::unique_ptr m_x; 334 | bool m_prev; 335 | int m_current, m_states; 336 | }; 337 | 338 | class ValueEdge : public ValueExpr 339 | { 340 | public: 341 | ValueEdge(std::unique_ptr x) 342 | :m_x(std::move(x)), m_prev(false) 343 | { 344 | } 345 | value_t get_value() override 346 | { 347 | bool x = m_x->get_value() != 0; 348 | value_t res = (!m_prev && x); //edge on 349 | m_prev = x; 350 | return res; 351 | } 352 | private: 353 | std::unique_ptr m_x; 354 | bool m_prev; 355 | }; 356 | 357 | class ValueHypot : public ValueExpr 358 | { 359 | public: 360 | ValueHypot(std::vector> &&exprs) 361 | :m_exprs(std::move(exprs)) 362 | { 363 | } 364 | value_t get_value() override 365 | { 366 | value_t res = 0; 367 | for (auto &e: m_exprs) 368 | { 369 | value_t x = e->get_value(); 370 | res += x*x; 371 | } 372 | return sqrt(res); 373 | } 374 | bool is_constant() const override 375 | { 376 | for (auto &e: m_exprs) 377 | if (!e->is_constant()) 378 | return false; 379 | return true; 380 | } 381 | private: 382 | std::vector> m_exprs; 383 | }; 384 | 385 | class ValueAtan2 : public ValueExpr 386 | { 387 | public: 388 | ValueAtan2(std::unique_ptr y, std::unique_ptr x) 389 | :m_y(std::move(y)), m_x(std::move(x)) 390 | { 391 | } 392 | value_t get_value() override 393 | { 394 | value_t y = m_y->get_value(); 395 | value_t x = m_x->get_value(); 396 | return atan2(y, x); 397 | } 398 | bool is_constant() const override 399 | { 400 | return m_y->is_constant() && m_x->is_constant(); 401 | } 402 | private: 403 | std::unique_ptr m_y, m_x; 404 | }; 405 | 406 | class ValueQuaternion : public ValueExpr 407 | { 408 | private: 409 | typedef ::Quaternion Quaternion; 410 | 411 | public: 412 | ValueQuaternion(std::unique_ptr trig, std::unique_ptr w, std::unique_ptr x, std::unique_ptr y, std::unique_ptr z) 413 | :m_trig(std::move(trig)), m_w(std::move(w)), m_x(std::move(x)), m_y(std::move(y)), m_z(std::move(z)), m_triggered(trig == nullptr) 414 | { 415 | } 416 | value_t get_value() override 417 | { 418 | if (m_trig) 419 | { 420 | value_t triggered = m_trig->get_value(); 421 | if (!triggered) 422 | { 423 | m_triggered = false; 424 | m_roll = m_yaw = m_pitch = 0; 425 | return 0; 426 | } 427 | } 428 | 429 | //Steam axes are: 430 | // * X is right 431 | // * Y is forward 432 | // * Z is up 433 | //But to do proper roll/pitch/yaw we need: 434 | // * X is forward 435 | // * Y is right 436 | // * Z is up 437 | //So we just swap X and Y. We also have to change the sign of W to avoid changing the chirality. 438 | 439 | value_t w = -m_w->get_value(); 440 | value_t x = m_y->get_value(); 441 | value_t y = m_x->get_value(); 442 | value_t z = m_z->get_value(); 443 | Quaternion qt(w, x, y, z); 444 | 445 | if (!m_triggered) 446 | { 447 | m_triggered = true; 448 | m_quat0 = Conjugate(qt); 449 | return 0; 450 | } 451 | 452 | Quaternion qd = m_quat0 * qt; 453 | qd.ToAngles(m_roll, m_pitch, m_yaw); 454 | //printf("Q: roll=%f pitch=%f yaw=%f\n", m_roll, m_pitch, m_yaw); 455 | 456 | //value_t ax, ay, az, aa; 457 | //qd.ToAxis(ax, ay, az, aa); 458 | //printf("Q: X=%f Y=%f Z=%f Ang=%f\n", ax, ay, az, aa); 459 | return m_roll; 460 | } 461 | value_t get_field(Field field) override 462 | { 463 | switch (field) 464 | { 465 | case Field::Roll: 466 | return m_roll; 467 | case Field::Pitch: 468 | return m_pitch; 469 | case Field::Yaw: 470 | return m_yaw; 471 | default: 472 | return 0; 473 | } 474 | } 475 | 476 | private: 477 | std::unique_ptr m_trig, m_w, m_x, m_y, m_z; 478 | bool m_triggered; 479 | Quaternion m_quat0; 480 | value_t m_roll, m_pitch, m_yaw; 481 | }; 482 | 483 | class ValuePolar : public ValueExpr 484 | { 485 | public: 486 | ValuePolar(std::unique_ptr x, std::unique_ptr y) 487 | :m_x(std::move(x)), m_y(std::move(y)), m_angle(0), m_radius(0) 488 | { 489 | } 490 | void add_rotation(std::unique_ptr rot) 491 | { 492 | if (!m_rotation) 493 | m_rotation = std::move(rot); 494 | else 495 | m_rotation = std::unique_ptr(new ValueOper(InputToken_PLUS, m_rotation.release(), rot.release())); 496 | } 497 | value_t get_value() override 498 | { 499 | value_t y = m_y->get_value(); 500 | value_t x = m_x->get_value(); 501 | value_t rot = m_rotation? m_rotation->get_value() : 0; 502 | m_angle = atan2(y, x) + rot; 503 | m_radius = hypot(x, y); 504 | return m_angle; 505 | } 506 | value_t get_field(Field field) override 507 | { 508 | switch (field) 509 | { 510 | case Field::X: 511 | return m_x->get_value(); 512 | case Field::Y: 513 | return m_y->get_value(); 514 | case Field::Angle: 515 | return m_angle; 516 | case Field::Radius: 517 | return m_radius; 518 | default: 519 | return 0; 520 | } 521 | } 522 | value_t get_angle() 523 | { 524 | return m_radius; 525 | } 526 | private: 527 | std::unique_ptr m_x, m_y, m_rotation; 528 | value_t m_angle, m_radius; 529 | }; 530 | 531 | class ValueField : public ValueExpr 532 | { 533 | public: 534 | ValueField(std::unique_ptr expr, Field field) 535 | :m_expr(std::move(expr)), m_field(field) 536 | { 537 | } 538 | value_t get_value() override 539 | { 540 | return m_expr->get_field(m_field); 541 | } 542 | private: 543 | std::unique_ptr m_expr; 544 | Field m_field; 545 | }; 546 | 547 | ValueExpr* create_func(const std::string &name, std::vector> &&exprs) 548 | { 549 | try 550 | { 551 | if (name == "bool") 552 | { 553 | return create_func_ex(func_bool, std::move(exprs)); 554 | } 555 | else if (name == "between") 556 | { 557 | return create_func_ex(func_between, std::move(exprs)); 558 | } 559 | else if (name == "between_angle") 560 | { 561 | return create_func_ex(func_between_angle, std::move(exprs)); 562 | } 563 | if (name == "deg") 564 | { 565 | if (exprs.size() != 1) 566 | throw std::runtime_error("wrong number of arguments in function"); 567 | return new ValueOper(InputToken_MULT, exprs[0].release(), new ValueConst(M_PI/180)); 568 | } 569 | else if (name == "mouse") 570 | { 571 | if (exprs.size() != 2) 572 | throw std::runtime_error("wrong number of arguments in function"); 573 | return new ValueMouse(std::move(exprs[0]), std::move(exprs[1])); 574 | } 575 | else if (name == "step") 576 | { 577 | if (exprs.size() != 2) 578 | throw std::runtime_error("wrong number of arguments in function"); 579 | return new ValueStep(std::move(exprs[0]), std::move(exprs[1])); 580 | } 581 | else if (name == "defuzz") 582 | { 583 | if (exprs.size() != 2) 584 | throw std::runtime_error("wrong number of arguments in function"); 585 | return new ValueDefuzz(std::move(exprs[0]), std::move(exprs[1])); 586 | } 587 | else if (name == "turbo") 588 | { 589 | if (exprs.size() != 1) 590 | throw std::runtime_error("wrong number of arguments in function"); 591 | return new ValueTurbo(std::move(exprs[0])); 592 | } 593 | else if (name == "toggle") 594 | { 595 | if (exprs.size() == 1) 596 | return new ValueToggle(std::move(exprs[0]), 2); 597 | if (exprs.size() > 2) 598 | throw std::runtime_error("wrong number of arguments in function"); 599 | if (!exprs[1]->is_constant()) 600 | throw std::runtime_error("second argument must be a constant"); 601 | int c = static_cast(exprs[1]->get_value()); 602 | if (c < 2) 603 | throw std::runtime_error("second argument must be >= 2"); 604 | return new ValueToggle(std::move(exprs[0]), c); 605 | } 606 | else if (name == "edge") 607 | { 608 | if (exprs.size() != 1) 609 | throw std::runtime_error("wrong number of arguments in function"); 610 | return new ValueEdge(std::move(exprs[0])); 611 | } 612 | else if (name == "hypot") 613 | { 614 | return new ValueHypot(std::move(exprs)); 615 | } 616 | else if (name == "atan2") 617 | { 618 | if (exprs.size() != 2) 619 | throw std::runtime_error("wrong number of arguments in function"); 620 | return new ValueAtan2(std::move(exprs[0]), std::move(exprs[1])); 621 | } 622 | else if (name == "quaternion") 623 | { 624 | switch (exprs.size()) 625 | { 626 | case 4: 627 | return new ValueQuaternion(nullptr, std::move(exprs[0]), std::move(exprs[1]), std::move(exprs[2]), std::move(exprs[3])); 628 | case 5: 629 | return new ValueQuaternion(std::move(exprs[0]), std::move(exprs[1]), std::move(exprs[2]), std::move(exprs[3]), std::move(exprs[4])); 630 | default: 631 | throw std::runtime_error("wrong number of arguments in function"); 632 | } 633 | } 634 | else if (name == "polar") 635 | { 636 | if (exprs.size() != 2) 637 | throw std::runtime_error("wrong number of arguments in function"); 638 | return new ValuePolar(std::move(exprs[0]), std::move(exprs[1])); 639 | } 640 | else if (name == "get_x") 641 | { 642 | if (exprs.size() != 1) 643 | throw std::runtime_error("wrong number of arguments in function"); 644 | return new ValueField(std::move(exprs[0]), ValueExpr::Field::X); 645 | } 646 | else if (name == "get_y") 647 | { 648 | if (exprs.size() != 1) 649 | throw std::runtime_error("wrong number of arguments in function"); 650 | return new ValueField(std::move(exprs[0]), ValueExpr::Field::Y); 651 | } 652 | else if (name == "get_z") 653 | { 654 | if (exprs.size() != 1) 655 | throw std::runtime_error("wrong number of arguments in function"); 656 | return new ValueField(std::move(exprs[0]), ValueExpr::Field::Z); 657 | } 658 | else if (name == "get_roll") 659 | { 660 | if (exprs.size() != 1) 661 | throw std::runtime_error("wrong number of arguments in function"); 662 | return new ValueField(std::move(exprs[0]), ValueExpr::Field::Roll); 663 | } 664 | else if (name == "get_pitch") 665 | { 666 | if (exprs.size() != 1) 667 | throw std::runtime_error("wrong number of arguments in function"); 668 | return new ValueField(std::move(exprs[0]), ValueExpr::Field::Pitch); 669 | } 670 | else if (name == "get_yaw") 671 | { 672 | if (exprs.size() != 1) 673 | throw std::runtime_error("wrong number of arguments in function"); 674 | return new ValueField(std::move(exprs[0]), ValueExpr::Field::Yaw); 675 | } 676 | else if (name == "get_angle") 677 | { 678 | if (exprs.size() != 1) 679 | throw std::runtime_error("wrong number of arguments in function"); 680 | return new ValueField(std::move(exprs[0]), ValueExpr::Field::Angle); 681 | } 682 | else if (name == "get_radius") 683 | { 684 | if (exprs.size() != 1) 685 | throw std::runtime_error("wrong number of arguments in function"); 686 | return new ValueField(std::move(exprs[0]), ValueExpr::Field::Radius); 687 | } 688 | else if (name == "rotate") 689 | { 690 | if (exprs.size() != 2) 691 | throw std::runtime_error("wrong number of arguments in function"); 692 | auto polar = dynamic_cast(exprs[0].get()); 693 | if (!polar) 694 | throw std::runtime_error("argument to 'rotate' must be a polar value"); 695 | polar->add_rotation(std::move(exprs[1])); 696 | return exprs[0].release(); 697 | } 698 | else 699 | throw std::runtime_error("unknown function"); 700 | } 701 | catch (std::runtime_error &e) 702 | { 703 | throw std::runtime_error(std::string(e.what()) + ": " + name); 704 | } 705 | return 0; 706 | } 707 | 708 | //////////////////// 709 | 710 | enum class CharCategory 711 | { 712 | Space, 713 | Letter, 714 | Number, 715 | Other, 716 | }; 717 | 718 | CharCategory char_category(char c) 719 | { 720 | if ((c >= '0' && c <= '9')) 721 | return CharCategory::Number; 722 | else if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_') 723 | return CharCategory::Letter; 724 | else if (c == ' ' || c == '\t' || c == '\n' || c == '\r') 725 | return CharCategory::Space; 726 | else 727 | return CharCategory::Other; 728 | } 729 | 730 | CharCategory next_token(const std::string &txt, size_t &pos) 731 | { 732 | char c = txt[pos++]; 733 | 734 | CharCategory cc = char_category(c); 735 | if (cc == CharCategory::Other) 736 | return cc; 737 | 738 | for (; pos < txt.size(); ++pos) 739 | { 740 | char c2 = txt[pos]; 741 | if (cc == CharCategory::Number && c2 == '.') 742 | continue; 743 | CharCategory cc2 = char_category(c2); 744 | if (cc == CharCategory::Letter && cc2 == CharCategory::Number) 745 | continue; 746 | if (cc2 != cc) 747 | break; 748 | } 749 | return cc; 750 | } 751 | 752 | ValueRef *create_value_ref(const std::string &sdev, const std::string &saxis, IInputByName &finder) 753 | { 754 | auto dev = finder.find_input(sdev); 755 | if (!dev) 756 | throw std::runtime_error("unknown device in ref: " + sdev + "." + saxis); 757 | auto value_id = dev->parse_value(saxis); 758 | return new ValueRef(dev, value_id); 759 | } 760 | 761 | std::unique_ptr parse_ref(const std::string &desc, IInputByName &finder) 762 | { 763 | DevInputArgs args{ finder }; 764 | void *parser = DevInputParseAlloc(malloc); 765 | size_t pos = 0; 766 | while (pos < desc.size() && !args.error) 767 | { 768 | size_t pos0 = pos; 769 | CharCategory cc = next_token(desc, pos); 770 | //printf("%d: %d %d <%s>\n", (int)cc, pos0, pos, std::string(desc, pos0, pos-pos0).c_str()); 771 | switch (cc) 772 | { 773 | case CharCategory::Space: 774 | break; 775 | case CharCategory::Letter: 776 | { 777 | std::string s(desc, pos0, pos - pos0); 778 | if (s == "and") 779 | DevInputParse(parser, InputToken_AND, 0, &args); 780 | else if (s == "or") 781 | DevInputParse(parser, InputToken_OR, 0, &args); 782 | else if (s == "not") 783 | DevInputParse(parser, InputToken_NOT, 0, &args); 784 | else if (s == "pi") 785 | DevInputParse(parser, InputToken_PI, 0, &args); 786 | else 787 | DevInputParse(parser, InputToken_NAME, new std::string(std::move(s)), &args); 788 | } 789 | break; 790 | case CharCategory::Number: 791 | { 792 | std::string *s = new std::string(desc, pos0, pos - pos0); 793 | DevInputParse(parser, InputToken_NUMBER, s, &args); 794 | } 795 | break; 796 | case CharCategory::Other: 797 | switch (desc[pos0]) 798 | { 799 | case '(': 800 | DevInputParse(parser, InputToken_LPAREN, 0, &args); 801 | break; 802 | case ')': 803 | DevInputParse(parser, InputToken_RPAREN, 0, &args); 804 | break; 805 | case '.': 806 | DevInputParse(parser, InputToken_PERIOD, 0, &args); 807 | break; 808 | case ',': 809 | DevInputParse(parser, InputToken_COMMA, 0, &args); 810 | break; 811 | case ':': 812 | DevInputParse(parser, InputToken_COLON, 0, &args); 813 | break; 814 | case '?': 815 | DevInputParse(parser, InputToken_QUESTION, 0, &args); 816 | break; 817 | case '+': 818 | DevInputParse(parser, InputToken_PLUS, 0, &args); 819 | break; 820 | case '-': 821 | DevInputParse(parser, InputToken_MINUS, 0, &args); 822 | break; 823 | case '>': 824 | DevInputParse(parser, InputToken_GT, 0, &args); 825 | break; 826 | case '<': 827 | DevInputParse(parser, InputToken_LT, 0, &args); 828 | break; 829 | case '*': 830 | DevInputParse(parser, InputToken_MULT, 0, &args); 831 | break; 832 | case '/': 833 | DevInputParse(parser, InputToken_DIV, 0, &args); 834 | break; 835 | case '$': 836 | DevInputParse(parser, InputToken_DOLLAR, 0, &args); 837 | break; 838 | default: 839 | printf("*** unknown character %c\n", desc[pos0]); 840 | break; 841 | } 842 | break; 843 | } 844 | } 845 | DevInputParse(parser, 0, 0, &args); 846 | DevInputParseFree(parser, free); 847 | 848 | if (args.error || !args.input) 849 | throw std::runtime_error("invalid input expression: " + desc); 850 | 851 | return std::unique_ptr(args.input); 852 | } 853 | 854 | ValueExpr* optimize(ValueExpr *expr) 855 | { 856 | //I will only do basic optimizations. No bytecode or anything fancy. 857 | //Not that it will have a big performance gain, I'm doing it just for show. 858 | 859 | //For now, only constant folding: 860 | if (expr->is_constant()) 861 | { 862 | //Already a constant, no folding over itself 863 | if (dynamic_cast(expr)) 864 | return expr; 865 | 866 | value_t a = expr->get_value(); 867 | //printf("Constant folding: %d\n", a); 868 | delete expr; 869 | //no further optimizations on constant values 870 | return new ValueConst(a); 871 | } 872 | 873 | return expr; 874 | } 875 | 876 | -------------------------------------------------------------------------------- /devinput-parser.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017, Rodrigo Rivas Costa 4 | 5 | This file is part of inputmap. 6 | 7 | inputmap is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | inputmap is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with inputmap. If not, see . 19 | 20 | */ 21 | 22 | #ifndef DEVINPUT_PARSER_H_INCLUDED 23 | #define DEVINPUT_PARSER_H_INCLUDED 24 | 25 | #include 26 | #include 27 | #include "inputdev.h" 28 | 29 | struct ValueExpr 30 | { 31 | enum class Field 32 | { 33 | X, 34 | Y, 35 | Z, 36 | Yaw, 37 | Pitch, 38 | Roll, 39 | Angle, 40 | Radius, 41 | }; 42 | virtual ~ValueExpr() {} 43 | virtual value_t get_value() =0; 44 | virtual value_t get_field(Field field) 45 | { return 0; } 46 | virtual bool is_constant() const 47 | { return false; } 48 | }; 49 | 50 | class Variable 51 | { 52 | public: 53 | Variable(std::unique_ptr e) 54 | :m_expr(std::move(e)), m_value(0) 55 | {} 56 | void evaluate() 57 | { 58 | m_value = m_expr->get_value(); 59 | } 60 | bool is_constant() const 61 | { 62 | return m_expr->is_constant(); 63 | } 64 | value_t get_value() const 65 | { 66 | return m_value; 67 | } 68 | value_t get_field(ValueExpr::Field field) const 69 | { 70 | //fields are not cached 71 | return m_expr->get_field(field); 72 | } 73 | private: 74 | std::unique_ptr m_expr; 75 | value_t m_value; 76 | }; 77 | 78 | struct IInputByName 79 | { 80 | virtual ~IInputByName() {} 81 | virtual std::shared_ptr find_input(const std::string &name) =0; 82 | virtual Variable *find_variable(const std::string &name) =0; 83 | }; 84 | 85 | struct DevInputArgs 86 | { 87 | IInputByName &finder; 88 | ValueExpr *input; 89 | bool error; 90 | }; 91 | 92 | void *DevInputParseAlloc(void *(*mallocProc)(size_t)); 93 | void DevInputParseFree(void *p, void (*freeProc)(void*)); 94 | void DevInputParse(void *yyp, int yymajor, const std::string *yyminor, DevInputArgs *args); 95 | 96 | class ValueConst : public ValueExpr 97 | { 98 | public: 99 | ValueConst(value_t val) 100 | :m_value(val) 101 | { 102 | } 103 | value_t get_value() override { return m_value; } 104 | bool is_constant() const override 105 | { return true; } 106 | private: 107 | value_t m_value; 108 | }; 109 | 110 | class ValueRef : public ValueExpr 111 | { 112 | public: 113 | ValueRef(std::shared_ptr dev, ValueId id) 114 | :m_device(dev), m_value_id(id) 115 | { 116 | } 117 | value_t get_value() override; 118 | std::shared_ptr get_device() 119 | { 120 | return m_device.lock(); 121 | } 122 | const ValueId &get_value_id() const 123 | { 124 | return m_value_id; 125 | } 126 | private: 127 | std::weak_ptr m_device; 128 | ValueId m_value_id; 129 | }; 130 | 131 | class ValueCond : public ValueExpr 132 | { 133 | public: 134 | ValueCond(ValueExpr *c, ValueExpr *t, ValueExpr *f) 135 | :m_cond(c), m_true(t), m_false(f) 136 | { 137 | } 138 | value_t get_value() override; 139 | bool is_constant() const override; 140 | private: 141 | std::unique_ptr m_cond, m_true, m_false; 142 | }; 143 | 144 | class ValueOper : public ValueExpr 145 | { 146 | public: 147 | ValueOper(int oper, ValueExpr *l, ValueExpr *r) 148 | :m_oper(oper), m_left(l), m_right(r) 149 | { 150 | } 151 | value_t get_value() override; 152 | bool is_constant() const override; 153 | private: 154 | int m_oper; 155 | std::unique_ptr m_left, m_right; 156 | }; 157 | 158 | class ValueUnary : public ValueExpr 159 | { 160 | public: 161 | ValueUnary(int oper, ValueExpr *e) 162 | :m_oper(oper), m_expr(e) 163 | { 164 | } 165 | value_t get_value() override; 166 | bool is_constant() const override 167 | { return m_expr->is_constant(); } 168 | private: 169 | int m_oper; 170 | std::unique_ptr m_expr; 171 | }; 172 | 173 | class ValueVariable : public ValueExpr 174 | { 175 | public: 176 | ValueVariable(const Variable *var) 177 | :m_var(var) 178 | { 179 | } 180 | value_t get_value() override 181 | { return m_var->get_value(); } 182 | value_t get_field(ValueExpr::Field field) override 183 | { return m_var->get_field(field); } 184 | bool is_constant() const override 185 | { return m_var->is_constant(); } 186 | private: 187 | const Variable *m_var; 188 | }; 189 | 190 | ValueRef *create_value_ref(const std::string &sdev, const std::string &saxis, IInputByName &finder); 191 | ValueExpr* create_func(const std::string &name, std::vector> &&exprs); 192 | 193 | std::unique_ptr parse_ref(const std::string &desc, IInputByName &finder); 194 | ValueExpr* optimize(ValueExpr *expr); 195 | 196 | #endif /* DEVINPUT_PARSER_H_INCLUDED */ 197 | 198 | -------------------------------------------------------------------------------- /devinput.lem: -------------------------------------------------------------------------------- 1 | %include { 2 | 3 | #include 4 | #include 5 | #include 6 | #include "devinput-parser.h" 7 | #include "devinput.h" 8 | 9 | #define LOCAL(x, X) \ 10 | auto x{std::move(*X)}; \ 11 | delete X 12 | 13 | } 14 | 15 | %name DevInputParse 16 | %token_prefix InputToken_ 17 | 18 | %parse_accept { 19 | } 20 | 21 | %parse_failure { 22 | args->error = true; 23 | } 24 | 25 | %syntax_error { 26 | args->error = true; 27 | } 28 | 29 | %extra_argument { DevInputArgs *__attribute__((unused)) args } 30 | %token_type { const std::string * } 31 | %token_destructor { delete $$; } 32 | 33 | %type expr { ValueExpr * } 34 | %destructor expr { delete $$; } 35 | 36 | %type expr_ { ValueExpr * } 37 | %destructor expr_ { delete $$; } 38 | 39 | %type value_ref { ValueRef * } 40 | %destructor value_ref { delete $$; } 41 | 42 | %type value_const { ValueConst * } 43 | %destructor value_const { delete $$; } 44 | 45 | %type expr_comma_list { std::vector> * } 46 | %destructor expr_comma_list { delete $$; } 47 | 48 | %type variable { Variable * } 49 | //variable has no destructor because they live globally 50 | 51 | %right QUESTION. 52 | %left COMMA. 53 | %left OR. 54 | %left AND. 55 | %nonassoc GT LT. 56 | %left PLUS MINUS. 57 | %left MULT DIV. 58 | %nonassoc NOT. 59 | 60 | input ::= expr(A). { args->input = A; } 61 | 62 | expr(A) ::= expr_(B). { A = optimize(B); } 63 | 64 | expr_(A) ::= LPAREN expr(B) RPAREN. { A = B; } 65 | expr_(A) ::= value_ref(B). { A = B; } 66 | expr_(A) ::= value_const(B). { A = B; } 67 | expr_(A) ::= variable(B). { A = new ValueVariable(B); } 68 | expr_(A) ::= expr(B) QUESTION expr(C) COLON expr(D). { A = new ValueCond(B, C, D); } 69 | 70 | expr_(A) ::= expr(B) PLUS expr(C). { A = new ValueOper(InputToken_PLUS, B, C); } 71 | expr_(A) ::= expr(B) MINUS expr(C). { A = new ValueOper(InputToken_MINUS, B, C); } 72 | expr_(A) ::= expr(B) AND expr(C). { A = new ValueOper(InputToken_AND, B, C); } 73 | expr_(A) ::= expr(B) OR expr(C). { A = new ValueOper(InputToken_OR, B, C); } 74 | expr_(A) ::= expr(B) GT expr(C). { A = new ValueOper(InputToken_GT, B, C); } 75 | expr_(A) ::= expr(B) LT expr(C). { A = new ValueOper(InputToken_LT, B, C); } 76 | expr_(A) ::= expr(B) MULT expr(C). { A = new ValueOper(InputToken_MULT, B, C); } 77 | expr_(A) ::= expr(B) DIV expr(C). { A = new ValueOper(InputToken_DIV, B, C); } 78 | expr_(A) ::= MINUS expr(B). [NOT] { A = new ValueUnary(InputToken_MINUS, B); } 79 | expr_(A) ::= NOT expr(B). { A = new ValueUnary(InputToken_NOT, B); } 80 | expr_(A) ::= NAME(B) LPAREN expr_comma_list(C) RPAREN. { 81 | LOCAL(b, B); 82 | LOCAL(c, C); 83 | A = create_func(std::move(b), std::move(c)); 84 | } 85 | 86 | value_ref(A) ::= NAME(B) PERIOD NAME(C). { 87 | LOCAL(b, B); 88 | LOCAL(c, C); 89 | A = create_value_ref(b, c, args->finder); 90 | } 91 | 92 | value_const(A) ::= NUMBER(B). { 93 | LOCAL(b, B); 94 | std::istringstream is(b); 95 | value_t val; 96 | if (!(is >> val)) 97 | throw std::runtime_error("invalid number: " + b); 98 | A = new ValueConst(val); 99 | } 100 | 101 | value_const(A) ::= PI. { 102 | A = new ValueConst(M_PI); 103 | } 104 | 105 | variable(A) ::= DOLLAR NAME(B). { 106 | LOCAL(b, B); 107 | Variable *v = args->finder.find_variable(b); 108 | if (!v) 109 | throw std::runtime_error("undefined variable: " + b); 110 | A = v; 111 | } 112 | 113 | expr_comma_list(A) ::= expr(B). { A = new std::vector>(); A->emplace_back(B); } 114 | expr_comma_list(A) ::= expr_comma_list(B) COMMA expr(C). { B->emplace_back(C); A = B; } 115 | -------------------------------------------------------------------------------- /event-codes.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017, Rodrigo Rivas Costa 4 | 5 | This file is part of inputmap. 6 | 7 | inputmap is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | inputmap is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with inputmap. If not, see . 19 | 20 | */ 21 | 22 | #include "event-codes.h" 23 | 24 | #define BeginKey() EventName g_key_names[KEY_CNT] = { 25 | #define BitKey(x) {x, #x}, 26 | #define BitKey2(x,y) {x, #x}, 27 | #define EndKey() }; 28 | 29 | #define BeginRel() EventName g_rel_names[REL_CNT] = { 30 | #define BitRel(x) {x, #x}, 31 | #define EndRel() }; 32 | 33 | #define BeginAbs() EventName g_abs_names[ABS_CNT] = { 34 | #define BitAbs(x) {x, #x}, 35 | #define EndAbs() }; 36 | 37 | #define BeginFF() EventName g_ff_names[FF_CNT] = { 38 | #define BitFF(x) {x, #x}, 39 | #define EndFF() }; 40 | 41 | #include "event-codes.inc" 42 | 43 | 44 | -------------------------------------------------------------------------------- /event-codes.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017, Rodrigo Rivas Costa 4 | 5 | This file is part of inputmap. 6 | 7 | inputmap is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | inputmap is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with inputmap. If not, see . 19 | 20 | */ 21 | 22 | #ifndef EVENT_CODES_H_INCLUDED 23 | #define EVENT_CODES_H_INCLUDED 24 | 25 | #include 26 | #include 27 | 28 | struct EventName 29 | { 30 | int id; 31 | const char *name; 32 | }; 33 | 34 | extern EventName g_key_names[KEY_CNT]; 35 | extern EventName g_rel_names[REL_CNT]; 36 | extern EventName g_abs_names[ABS_CNT]; 37 | extern EventName g_ff_names[FF_CNT]; 38 | 39 | #endif /* EVENT-CODES_H_INCLUDED */ 40 | -------------------------------------------------------------------------------- /event-codes.inc: -------------------------------------------------------------------------------- 1 | #ifndef BeginEv 2 | #define BeginEv() 3 | #endif 4 | #ifndef BitEv 5 | #define BitEv(x) 6 | #endif 7 | #ifndef EndEv 8 | #define EndEv() 9 | #endif 10 | 11 | #ifndef BeginSyn 12 | #define BeginSyn() 13 | #endif 14 | #ifndef BitSyn 15 | #define BitSyn(x) 16 | #endif 17 | #ifndef EndSyn 18 | #define EndSyn() 19 | #endif 20 | 21 | #ifndef BeginKey 22 | #define BeginKey() 23 | #endif 24 | #ifndef BitKey 25 | #define BitKey(x) 26 | #define BitKey2(x,y) 27 | #endif 28 | #ifndef EndKey 29 | #define EndKey() 30 | #endif 31 | 32 | #ifndef BeginRel 33 | #define BeginRel() 34 | #endif 35 | #ifndef BitRel 36 | #define BitRel(x) 37 | #endif 38 | #ifndef EndRel 39 | #define EndRel() 40 | #endif 41 | 42 | #ifndef BeginAbs 43 | #define BeginAbs() 44 | #endif 45 | #ifndef BitAbs 46 | #define BitAbs(x) 47 | #endif 48 | #ifndef EndAbs 49 | #define EndAbs() 50 | #endif 51 | 52 | #ifndef BeginSw 53 | #define BeginSw() 54 | #endif 55 | #ifndef BitSw 56 | #define BitSw(x) 57 | #define BitSw2(x,y) 58 | #endif 59 | #ifndef EndSw 60 | #define EndSw() 61 | #endif 62 | 63 | #ifndef BeginMsc 64 | #define BeginMsc() 65 | #endif 66 | #ifndef BitMsc 67 | #define BitMsc(x) 68 | #endif 69 | #ifndef EndMsc 70 | #define EndMsc() 71 | #endif 72 | 73 | #ifndef BeginLed 74 | #define BeginLed() 75 | #endif 76 | #ifndef BitLed 77 | #define BitLed(x) 78 | #endif 79 | #ifndef EndLed 80 | #define EndLed() 81 | #endif 82 | 83 | #ifndef BeginRep 84 | #define BeginRep() 85 | #endif 86 | #ifndef BitRep 87 | #define BitRep(x) 88 | #endif 89 | #ifndef EndRep 90 | #define EndRep() 91 | #endif 92 | 93 | #ifndef BeginSnd 94 | #define BeginSnd() 95 | #endif 96 | #ifndef BitSnd 97 | #define BitSnd(x) 98 | #endif 99 | #ifndef EndSnd 100 | #define EndSnd() 101 | #endif 102 | 103 | #ifndef BeginFF 104 | #define BeginFF() 105 | #endif 106 | #ifndef BitFF 107 | #define BitFF(x) 108 | #endif 109 | #ifndef EndFF 110 | #define EndFF() 111 | #endif 112 | 113 | 114 | 115 | BeginEv() 116 | BitEv(EV_SYN) 117 | BitEv(EV_KEY) 118 | BitEv(EV_REL) 119 | BitEv(EV_ABS) 120 | BitEv(EV_MSC) 121 | BitEv(EV_SW) 122 | BitEv(EV_LED) 123 | BitEv(EV_SND) 124 | BitEv(EV_REP) 125 | BitEv(EV_FF) 126 | BitEv(EV_PWR) 127 | BitEv(EV_FF_STATUS) 128 | EndEv() 129 | 130 | BeginSyn() 131 | BitSyn(SYN_REPORT) 132 | BitSyn(SYN_CONFIG) 133 | BitSyn(SYN_MT_REPORT) 134 | BitSyn(SYN_DROPPED) 135 | EndSyn() 136 | 137 | BeginKey() 138 | BitKey(KEY_RESERVED) 139 | BitKey(KEY_ESC) 140 | BitKey(KEY_1) 141 | BitKey(KEY_2) 142 | BitKey(KEY_3) 143 | BitKey(KEY_4) 144 | BitKey(KEY_5) 145 | BitKey(KEY_6) 146 | BitKey(KEY_7) 147 | BitKey(KEY_8) 148 | BitKey(KEY_9) 149 | BitKey(KEY_0) 150 | BitKey(KEY_MINUS) 151 | BitKey(KEY_EQUAL) 152 | BitKey(KEY_BACKSPACE) 153 | BitKey(KEY_TAB) 154 | BitKey(KEY_Q) 155 | BitKey(KEY_W) 156 | BitKey(KEY_E) 157 | BitKey(KEY_R) 158 | BitKey(KEY_T) 159 | BitKey(KEY_Y) 160 | BitKey(KEY_U) 161 | BitKey(KEY_I) 162 | BitKey(KEY_O) 163 | BitKey(KEY_P) 164 | BitKey(KEY_LEFTBRACE) 165 | BitKey(KEY_RIGHTBRACE) 166 | BitKey(KEY_ENTER) 167 | BitKey(KEY_LEFTCTRL) 168 | BitKey(KEY_A) 169 | BitKey(KEY_S) 170 | BitKey(KEY_D) 171 | BitKey(KEY_F) 172 | BitKey(KEY_G) 173 | BitKey(KEY_H) 174 | BitKey(KEY_J) 175 | BitKey(KEY_K) 176 | BitKey(KEY_L) 177 | BitKey(KEY_SEMICOLON) 178 | BitKey(KEY_APOSTROPHE) 179 | BitKey(KEY_GRAVE) 180 | BitKey(KEY_LEFTSHIFT) 181 | BitKey(KEY_BACKSLASH) 182 | BitKey(KEY_Z) 183 | BitKey(KEY_X) 184 | BitKey(KEY_C) 185 | BitKey(KEY_V) 186 | BitKey(KEY_B) 187 | BitKey(KEY_N) 188 | BitKey(KEY_M) 189 | BitKey(KEY_COMMA) 190 | BitKey(KEY_DOT) 191 | BitKey(KEY_SLASH) 192 | BitKey(KEY_RIGHTSHIFT) 193 | BitKey(KEY_KPASTERISK) 194 | BitKey(KEY_LEFTALT) 195 | BitKey(KEY_SPACE) 196 | BitKey(KEY_CAPSLOCK) 197 | BitKey(KEY_F1) 198 | BitKey(KEY_F2) 199 | BitKey(KEY_F3) 200 | BitKey(KEY_F4) 201 | BitKey(KEY_F5) 202 | BitKey(KEY_F6) 203 | BitKey(KEY_F7) 204 | BitKey(KEY_F8) 205 | BitKey(KEY_F9) 206 | BitKey(KEY_F10) 207 | BitKey(KEY_NUMLOCK) 208 | BitKey(KEY_SCROLLLOCK) 209 | BitKey(KEY_KP7) 210 | BitKey(KEY_KP8) 211 | BitKey(KEY_KP9) 212 | BitKey(KEY_KPMINUS) 213 | BitKey(KEY_KP4) 214 | BitKey(KEY_KP5) 215 | BitKey(KEY_KP6) 216 | BitKey(KEY_KPPLUS) 217 | BitKey(KEY_KP1) 218 | BitKey(KEY_KP2) 219 | BitKey(KEY_KP3) 220 | BitKey(KEY_KP0) 221 | BitKey(KEY_KPDOT) 222 | BitKey(KEY_ZENKAKUHANKAKU) 223 | BitKey(KEY_102ND) 224 | BitKey(KEY_F11) 225 | BitKey(KEY_F12) 226 | BitKey(KEY_RO) 227 | BitKey(KEY_KATAKANA) 228 | BitKey(KEY_HIRAGANA) 229 | BitKey(KEY_HENKAN) 230 | BitKey(KEY_KATAKANAHIRAGANA) 231 | BitKey(KEY_MUHENKAN) 232 | BitKey(KEY_KPJPCOMMA) 233 | BitKey(KEY_KPENTER) 234 | BitKey(KEY_RIGHTCTRL) 235 | BitKey(KEY_KPSLASH) 236 | BitKey(KEY_SYSRQ) 237 | BitKey(KEY_RIGHTALT) 238 | BitKey(KEY_LINEFEED) 239 | BitKey(KEY_HOME) 240 | BitKey(KEY_UP) 241 | BitKey(KEY_PAGEUP) 242 | BitKey(KEY_LEFT) 243 | BitKey(KEY_RIGHT) 244 | BitKey(KEY_END) 245 | BitKey(KEY_DOWN) 246 | BitKey(KEY_PAGEDOWN) 247 | BitKey(KEY_INSERT) 248 | BitKey(KEY_DELETE) 249 | BitKey(KEY_MACRO) 250 | BitKey(KEY_MUTE) 251 | BitKey(KEY_VOLUMEDOWN) 252 | BitKey(KEY_VOLUMEUP) 253 | BitKey(KEY_POWER) 254 | BitKey(KEY_KPEQUAL) 255 | BitKey(KEY_KPPLUSMINUS) 256 | BitKey(KEY_PAUSE) 257 | BitKey(KEY_SCALE) 258 | BitKey(KEY_KPCOMMA) 259 | BitKey(KEY_HANGEUL) 260 | BitKey2(KEY_HANGUEL, KEY_HANGEUL) 261 | BitKey(KEY_HANJA) 262 | BitKey(KEY_YEN) 263 | BitKey(KEY_LEFTMETA) 264 | BitKey(KEY_RIGHTMETA) 265 | BitKey(KEY_COMPOSE) 266 | BitKey(KEY_STOP) 267 | BitKey(KEY_AGAIN) 268 | BitKey(KEY_PROPS) 269 | BitKey(KEY_UNDO) 270 | BitKey(KEY_FRONT) 271 | BitKey(KEY_COPY) 272 | BitKey(KEY_OPEN) 273 | BitKey(KEY_PASTE) 274 | BitKey(KEY_FIND) 275 | BitKey(KEY_CUT) 276 | BitKey(KEY_HELP) 277 | BitKey(KEY_MENU) 278 | BitKey(KEY_CALC) 279 | BitKey(KEY_SETUP) 280 | BitKey(KEY_SLEEP) 281 | BitKey(KEY_WAKEUP) 282 | BitKey(KEY_FILE) 283 | BitKey(KEY_SENDFILE) 284 | BitKey(KEY_DELETEFILE) 285 | BitKey(KEY_XFER) 286 | BitKey(KEY_PROG1) 287 | BitKey(KEY_PROG2) 288 | BitKey(KEY_WWW) 289 | BitKey(KEY_MSDOS) 290 | BitKey(KEY_COFFEE) 291 | BitKey2(KEY_SCREENLOCK, KEY_COFFEE) 292 | BitKey(KEY_ROTATE_DISPLAY) 293 | BitKey2(KEY_DIRECTION, KEY_ROTATE_DISPLAY) 294 | BitKey(KEY_CYCLEWINDOWS) 295 | BitKey(KEY_MAIL) 296 | BitKey(KEY_BOOKMARKS) 297 | BitKey(KEY_COMPUTER) 298 | BitKey(KEY_BACK) 299 | BitKey(KEY_FORWARD) 300 | BitKey(KEY_CLOSECD) 301 | BitKey(KEY_EJECTCD) 302 | BitKey(KEY_EJECTCLOSECD) 303 | BitKey(KEY_NEXTSONG) 304 | BitKey(KEY_PLAYPAUSE) 305 | BitKey(KEY_PREVIOUSSONG) 306 | BitKey(KEY_STOPCD) 307 | BitKey(KEY_RECORD) 308 | BitKey(KEY_REWIND) 309 | BitKey(KEY_PHONE) 310 | BitKey(KEY_ISO) 311 | BitKey(KEY_CONFIG) 312 | BitKey(KEY_HOMEPAGE) 313 | BitKey(KEY_REFRESH) 314 | BitKey(KEY_EXIT) 315 | BitKey(KEY_MOVE) 316 | BitKey(KEY_EDIT) 317 | BitKey(KEY_SCROLLUP) 318 | BitKey(KEY_SCROLLDOWN) 319 | BitKey(KEY_KPLEFTPAREN) 320 | BitKey(KEY_KPRIGHTPAREN) 321 | BitKey(KEY_NEW) 322 | BitKey(KEY_REDO) 323 | BitKey(KEY_F13) 324 | BitKey(KEY_F14) 325 | BitKey(KEY_F15) 326 | BitKey(KEY_F16) 327 | BitKey(KEY_F17) 328 | BitKey(KEY_F18) 329 | BitKey(KEY_F19) 330 | BitKey(KEY_F20) 331 | BitKey(KEY_F21) 332 | BitKey(KEY_F22) 333 | BitKey(KEY_F23) 334 | BitKey(KEY_F24) 335 | BitKey(KEY_PLAYCD) 336 | BitKey(KEY_PAUSECD) 337 | BitKey(KEY_PROG3) 338 | BitKey(KEY_PROG4) 339 | BitKey(KEY_DASHBOARD) 340 | BitKey(KEY_SUSPEND) 341 | BitKey(KEY_CLOSE) 342 | BitKey(KEY_PLAY) 343 | BitKey(KEY_FASTFORWARD) 344 | BitKey(KEY_BASSBOOST) 345 | BitKey(KEY_PRINT) 346 | BitKey(KEY_HP) 347 | BitKey(KEY_CAMERA) 348 | BitKey(KEY_SOUND) 349 | BitKey(KEY_QUESTION) 350 | BitKey(KEY_EMAIL) 351 | BitKey(KEY_CHAT) 352 | BitKey(KEY_SEARCH) 353 | BitKey(KEY_CONNECT) 354 | BitKey(KEY_FINANCE) 355 | BitKey(KEY_SPORT) 356 | BitKey(KEY_SHOP) 357 | BitKey(KEY_ALTERASE) 358 | BitKey(KEY_CANCEL) 359 | BitKey(KEY_BRIGHTNESSDOWN) 360 | BitKey(KEY_BRIGHTNESSUP) 361 | BitKey(KEY_MEDIA) 362 | BitKey(KEY_SWITCHVIDEOMODE) 363 | BitKey(KEY_KBDILLUMTOGGLE) 364 | BitKey(KEY_KBDILLUMDOWN) 365 | BitKey(KEY_KBDILLUMUP) 366 | BitKey(KEY_SEND) 367 | BitKey(KEY_REPLY) 368 | BitKey(KEY_FORWARDMAIL) 369 | BitKey(KEY_SAVE) 370 | BitKey(KEY_DOCUMENTS) 371 | BitKey(KEY_BATTERY) 372 | BitKey(KEY_BLUETOOTH) 373 | BitKey(KEY_WLAN) 374 | BitKey(KEY_UWB) 375 | BitKey(KEY_UNKNOWN) 376 | BitKey(KEY_VIDEO_NEXT) 377 | BitKey(KEY_VIDEO_PREV) 378 | BitKey(KEY_BRIGHTNESS_CYCLE) 379 | BitKey(KEY_BRIGHTNESS_AUTO) 380 | BitKey2(KEY_BRIGHTNESS_ZERO, KEY_BRIGHTNESS_AUTO) 381 | BitKey(KEY_DISPLAY_OFF) 382 | BitKey(KEY_WWAN) 383 | BitKey2(KEY_WIMAX, KEY_WWAN) 384 | BitKey(KEY_RFKILL) 385 | BitKey(KEY_MICMUTE) 386 | BitKey(BTN_MISC) 387 | BitKey(BTN_0) 388 | BitKey(BTN_1) 389 | BitKey(BTN_2) 390 | BitKey(BTN_3) 391 | BitKey(BTN_4) 392 | BitKey(BTN_5) 393 | BitKey(BTN_6) 394 | BitKey(BTN_7) 395 | BitKey(BTN_8) 396 | BitKey(BTN_9) 397 | BitKey(BTN_MOUSE) 398 | BitKey(BTN_LEFT) 399 | BitKey(BTN_RIGHT) 400 | BitKey(BTN_MIDDLE) 401 | BitKey(BTN_SIDE) 402 | BitKey(BTN_EXTRA) 403 | BitKey(BTN_FORWARD) 404 | BitKey(BTN_BACK) 405 | BitKey(BTN_TASK) 406 | BitKey(BTN_JOYSTICK) 407 | BitKey(BTN_TRIGGER) 408 | BitKey(BTN_THUMB) 409 | BitKey(BTN_THUMB2) 410 | BitKey(BTN_TOP) 411 | BitKey(BTN_TOP2) 412 | BitKey(BTN_PINKIE) 413 | BitKey(BTN_BASE) 414 | BitKey(BTN_BASE2) 415 | BitKey(BTN_BASE3) 416 | BitKey(BTN_BASE4) 417 | BitKey(BTN_BASE5) 418 | BitKey(BTN_BASE6) 419 | BitKey(BTN_DEAD) 420 | BitKey(BTN_GAMEPAD) 421 | BitKey(BTN_SOUTH) 422 | BitKey2(BTN_A, BTN_SOUTH) 423 | BitKey(BTN_EAST) 424 | BitKey2(BTN_B, BTN_EAST) 425 | BitKey(BTN_C) 426 | BitKey(BTN_NORTH) 427 | BitKey2(BTN_X, BTN_NORTH) 428 | BitKey(BTN_WEST) 429 | BitKey2(BTN_Y, BTN_WEST) 430 | BitKey(BTN_Z) 431 | BitKey(BTN_TL) 432 | BitKey(BTN_TR) 433 | BitKey(BTN_TL2) 434 | BitKey(BTN_TR2) 435 | BitKey(BTN_SELECT) 436 | BitKey(BTN_START) 437 | BitKey(BTN_MODE) 438 | BitKey(BTN_THUMBL) 439 | BitKey(BTN_THUMBR) 440 | BitKey(BTN_DIGI) 441 | BitKey(BTN_TOOL_PEN) 442 | BitKey(BTN_TOOL_RUBBER) 443 | BitKey(BTN_TOOL_BRUSH) 444 | BitKey(BTN_TOOL_PENCIL) 445 | BitKey(BTN_TOOL_AIRBRUSH) 446 | BitKey(BTN_TOOL_FINGER) 447 | BitKey(BTN_TOOL_MOUSE) 448 | BitKey(BTN_TOOL_LENS) 449 | BitKey(BTN_TOOL_QUINTTAP) 450 | BitKey(BTN_TOUCH) 451 | BitKey(BTN_STYLUS) 452 | BitKey(BTN_STYLUS2) 453 | BitKey(BTN_TOOL_DOUBLETAP) 454 | BitKey(BTN_TOOL_TRIPLETAP) 455 | BitKey(BTN_TOOL_QUADTAP) 456 | BitKey(BTN_WHEEL) 457 | BitKey(BTN_GEAR_DOWN) 458 | BitKey(BTN_GEAR_UP) 459 | BitKey(KEY_OK) 460 | BitKey(KEY_SELECT) 461 | BitKey(KEY_GOTO) 462 | BitKey(KEY_CLEAR) 463 | BitKey(KEY_POWER2) 464 | BitKey(KEY_OPTION) 465 | BitKey(KEY_INFO) 466 | BitKey(KEY_TIME) 467 | BitKey(KEY_VENDOR) 468 | BitKey(KEY_ARCHIVE) 469 | BitKey(KEY_PROGRAM) 470 | BitKey(KEY_CHANNEL) 471 | BitKey(KEY_FAVORITES) 472 | BitKey(KEY_EPG) 473 | BitKey(KEY_PVR) 474 | BitKey(KEY_MHP) 475 | BitKey(KEY_LANGUAGE) 476 | BitKey(KEY_TITLE) 477 | BitKey(KEY_SUBTITLE) 478 | BitKey(KEY_ANGLE) 479 | BitKey(KEY_ZOOM) 480 | BitKey(KEY_MODE) 481 | BitKey(KEY_KEYBOARD) 482 | BitKey(KEY_SCREEN) 483 | BitKey(KEY_PC) 484 | BitKey(KEY_TV) 485 | BitKey(KEY_TV2) 486 | BitKey(KEY_VCR) 487 | BitKey(KEY_VCR2) 488 | BitKey(KEY_SAT) 489 | BitKey(KEY_SAT2) 490 | BitKey(KEY_CD) 491 | BitKey(KEY_TAPE) 492 | BitKey(KEY_RADIO) 493 | BitKey(KEY_TUNER) 494 | BitKey(KEY_PLAYER) 495 | BitKey(KEY_TEXT) 496 | BitKey(KEY_DVD) 497 | BitKey(KEY_AUX) 498 | BitKey(KEY_MP3) 499 | BitKey(KEY_AUDIO) 500 | BitKey(KEY_VIDEO) 501 | BitKey(KEY_DIRECTORY) 502 | BitKey(KEY_LIST) 503 | BitKey(KEY_MEMO) 504 | BitKey(KEY_CALENDAR) 505 | BitKey(KEY_RED) 506 | BitKey(KEY_GREEN) 507 | BitKey(KEY_YELLOW) 508 | BitKey(KEY_BLUE) 509 | BitKey(KEY_CHANNELUP) 510 | BitKey(KEY_CHANNELDOWN) 511 | BitKey(KEY_FIRST) 512 | BitKey(KEY_LAST) 513 | BitKey(KEY_AB) 514 | BitKey(KEY_NEXT) 515 | BitKey(KEY_RESTART) 516 | BitKey(KEY_SLOW) 517 | BitKey(KEY_SHUFFLE) 518 | BitKey(KEY_BREAK) 519 | BitKey(KEY_PREVIOUS) 520 | BitKey(KEY_DIGITS) 521 | BitKey(KEY_TEEN) 522 | BitKey(KEY_TWEN) 523 | BitKey(KEY_VIDEOPHONE) 524 | BitKey(KEY_GAMES) 525 | BitKey(KEY_ZOOMIN) 526 | BitKey(KEY_ZOOMOUT) 527 | BitKey(KEY_ZOOMRESET) 528 | BitKey(KEY_WORDPROCESSOR) 529 | BitKey(KEY_EDITOR) 530 | BitKey(KEY_SPREADSHEET) 531 | BitKey(KEY_GRAPHICSEDITOR) 532 | BitKey(KEY_PRESENTATION) 533 | BitKey(KEY_DATABASE) 534 | BitKey(KEY_NEWS) 535 | BitKey(KEY_VOICEMAIL) 536 | BitKey(KEY_ADDRESSBOOK) 537 | BitKey(KEY_MESSENGER) 538 | BitKey(KEY_DISPLAYTOGGLE) 539 | BitKey2(KEY_BRIGHTNESS_TOGGLE, KEY_DISPLAYTOGGLE) 540 | BitKey(KEY_SPELLCHECK) 541 | BitKey(KEY_LOGOFF) 542 | BitKey(KEY_DOLLAR) 543 | BitKey(KEY_EURO) 544 | BitKey(KEY_FRAMEBACK) 545 | BitKey(KEY_FRAMEFORWARD) 546 | BitKey(KEY_CONTEXT_MENU) 547 | BitKey(KEY_MEDIA_REPEAT) 548 | BitKey(KEY_10CHANNELSUP) 549 | BitKey(KEY_10CHANNELSDOWN) 550 | BitKey(KEY_IMAGES) 551 | BitKey(KEY_DEL_EOL) 552 | BitKey(KEY_DEL_EOS) 553 | BitKey(KEY_INS_LINE) 554 | BitKey(KEY_DEL_LINE) 555 | BitKey(KEY_FN) 556 | BitKey(KEY_FN_ESC) 557 | BitKey(KEY_FN_F1) 558 | BitKey(KEY_FN_F2) 559 | BitKey(KEY_FN_F3) 560 | BitKey(KEY_FN_F4) 561 | BitKey(KEY_FN_F5) 562 | BitKey(KEY_FN_F6) 563 | BitKey(KEY_FN_F7) 564 | BitKey(KEY_FN_F8) 565 | BitKey(KEY_FN_F9) 566 | BitKey(KEY_FN_F10) 567 | BitKey(KEY_FN_F11) 568 | BitKey(KEY_FN_F12) 569 | BitKey(KEY_FN_1) 570 | BitKey(KEY_FN_2) 571 | BitKey(KEY_FN_D) 572 | BitKey(KEY_FN_E) 573 | BitKey(KEY_FN_F) 574 | BitKey(KEY_FN_S) 575 | BitKey(KEY_FN_B) 576 | BitKey(KEY_BRL_DOT1) 577 | BitKey(KEY_BRL_DOT2) 578 | BitKey(KEY_BRL_DOT3) 579 | BitKey(KEY_BRL_DOT4) 580 | BitKey(KEY_BRL_DOT5) 581 | BitKey(KEY_BRL_DOT6) 582 | BitKey(KEY_BRL_DOT7) 583 | BitKey(KEY_BRL_DOT8) 584 | BitKey(KEY_BRL_DOT9) 585 | BitKey(KEY_BRL_DOT10) 586 | BitKey(KEY_NUMERIC_0) 587 | BitKey(KEY_NUMERIC_1) 588 | BitKey(KEY_NUMERIC_2) 589 | BitKey(KEY_NUMERIC_3) 590 | BitKey(KEY_NUMERIC_4) 591 | BitKey(KEY_NUMERIC_5) 592 | BitKey(KEY_NUMERIC_6) 593 | BitKey(KEY_NUMERIC_7) 594 | BitKey(KEY_NUMERIC_8) 595 | BitKey(KEY_NUMERIC_9) 596 | BitKey(KEY_NUMERIC_STAR) 597 | BitKey(KEY_NUMERIC_POUND) 598 | BitKey(KEY_NUMERIC_A) 599 | BitKey(KEY_NUMERIC_B) 600 | BitKey(KEY_NUMERIC_C) 601 | BitKey(KEY_NUMERIC_D) 602 | BitKey(KEY_CAMERA_FOCUS) 603 | BitKey(KEY_WPS_BUTTON) 604 | BitKey(KEY_TOUCHPAD_TOGGLE) 605 | BitKey(KEY_TOUCHPAD_ON) 606 | BitKey(KEY_TOUCHPAD_OFF) 607 | BitKey(KEY_CAMERA_ZOOMIN) 608 | BitKey(KEY_CAMERA_ZOOMOUT) 609 | BitKey(KEY_CAMERA_UP) 610 | BitKey(KEY_CAMERA_DOWN) 611 | BitKey(KEY_CAMERA_LEFT) 612 | BitKey(KEY_CAMERA_RIGHT) 613 | BitKey(KEY_ATTENDANT_ON) 614 | BitKey(KEY_ATTENDANT_OFF) 615 | BitKey(KEY_ATTENDANT_TOGGLE) 616 | BitKey(KEY_LIGHTS_TOGGLE) 617 | BitKey(BTN_DPAD_UP) 618 | BitKey(BTN_DPAD_DOWN) 619 | BitKey(BTN_DPAD_LEFT) 620 | BitKey(BTN_DPAD_RIGHT) 621 | BitKey(KEY_ALS_TOGGLE) 622 | BitKey(KEY_BUTTONCONFIG) 623 | BitKey(KEY_TASKMANAGER) 624 | BitKey(KEY_JOURNAL) 625 | BitKey(KEY_CONTROLPANEL) 626 | BitKey(KEY_APPSELECT) 627 | BitKey(KEY_SCREENSAVER) 628 | BitKey(KEY_VOICECOMMAND) 629 | BitKey(KEY_BRIGHTNESS_MIN) 630 | BitKey(KEY_BRIGHTNESS_MAX) 631 | BitKey(KEY_KBDINPUTASSIST_PREV) 632 | BitKey(KEY_KBDINPUTASSIST_NEXT) 633 | BitKey(KEY_KBDINPUTASSIST_PREVGROUP) 634 | BitKey(KEY_KBDINPUTASSIST_NEXTGROUP) 635 | BitKey(KEY_KBDINPUTASSIST_ACCEPT) 636 | BitKey(KEY_KBDINPUTASSIST_CANCEL) 637 | BitKey(KEY_RIGHT_UP) 638 | BitKey(KEY_RIGHT_DOWN) 639 | BitKey(KEY_LEFT_UP) 640 | BitKey(KEY_LEFT_DOWN) 641 | BitKey(KEY_ROOT_MENU) 642 | BitKey(KEY_MEDIA_TOP_MENU) 643 | BitKey(KEY_NUMERIC_11) 644 | BitKey(KEY_NUMERIC_12) 645 | BitKey(KEY_AUDIO_DESC) 646 | BitKey(KEY_3D_MODE) 647 | BitKey(KEY_NEXT_FAVORITE) 648 | BitKey(KEY_STOP_RECORD) 649 | BitKey(KEY_PAUSE_RECORD) 650 | BitKey(KEY_VOD) 651 | BitKey(KEY_UNMUTE) 652 | BitKey(KEY_FASTREVERSE) 653 | BitKey(KEY_SLOWREVERSE) 654 | BitKey(KEY_DATA) 655 | BitKey(BTN_TRIGGER_HAPPY) 656 | BitKey(BTN_TRIGGER_HAPPY1) 657 | BitKey(BTN_TRIGGER_HAPPY2) 658 | BitKey(BTN_TRIGGER_HAPPY3) 659 | BitKey(BTN_TRIGGER_HAPPY4) 660 | BitKey(BTN_TRIGGER_HAPPY5) 661 | BitKey(BTN_TRIGGER_HAPPY6) 662 | BitKey(BTN_TRIGGER_HAPPY7) 663 | BitKey(BTN_TRIGGER_HAPPY8) 664 | BitKey(BTN_TRIGGER_HAPPY9) 665 | BitKey(BTN_TRIGGER_HAPPY10) 666 | BitKey(BTN_TRIGGER_HAPPY11) 667 | BitKey(BTN_TRIGGER_HAPPY12) 668 | BitKey(BTN_TRIGGER_HAPPY13) 669 | BitKey(BTN_TRIGGER_HAPPY14) 670 | BitKey(BTN_TRIGGER_HAPPY15) 671 | BitKey(BTN_TRIGGER_HAPPY16) 672 | BitKey(BTN_TRIGGER_HAPPY17) 673 | BitKey(BTN_TRIGGER_HAPPY18) 674 | BitKey(BTN_TRIGGER_HAPPY19) 675 | BitKey(BTN_TRIGGER_HAPPY20) 676 | BitKey(BTN_TRIGGER_HAPPY21) 677 | BitKey(BTN_TRIGGER_HAPPY22) 678 | BitKey(BTN_TRIGGER_HAPPY23) 679 | BitKey(BTN_TRIGGER_HAPPY24) 680 | BitKey(BTN_TRIGGER_HAPPY25) 681 | BitKey(BTN_TRIGGER_HAPPY26) 682 | BitKey(BTN_TRIGGER_HAPPY27) 683 | BitKey(BTN_TRIGGER_HAPPY28) 684 | BitKey(BTN_TRIGGER_HAPPY29) 685 | BitKey(BTN_TRIGGER_HAPPY30) 686 | BitKey(BTN_TRIGGER_HAPPY31) 687 | BitKey(BTN_TRIGGER_HAPPY32) 688 | BitKey(BTN_TRIGGER_HAPPY33) 689 | BitKey(BTN_TRIGGER_HAPPY34) 690 | BitKey(BTN_TRIGGER_HAPPY35) 691 | BitKey(BTN_TRIGGER_HAPPY36) 692 | BitKey(BTN_TRIGGER_HAPPY37) 693 | BitKey(BTN_TRIGGER_HAPPY38) 694 | BitKey(BTN_TRIGGER_HAPPY39) 695 | BitKey(BTN_TRIGGER_HAPPY40) 696 | EndKey() 697 | 698 | BeginRel() 699 | BitRel(REL_X) 700 | BitRel(REL_Y) 701 | BitRel(REL_Z) 702 | BitRel(REL_RX) 703 | BitRel(REL_RY) 704 | BitRel(REL_RZ) 705 | BitRel(REL_HWHEEL) 706 | BitRel(REL_DIAL) 707 | BitRel(REL_WHEEL) 708 | BitRel(REL_MISC) 709 | EndRel() 710 | 711 | BeginAbs() 712 | BitAbs(ABS_X) 713 | BitAbs(ABS_Y) 714 | BitAbs(ABS_Z) 715 | BitAbs(ABS_RX) 716 | BitAbs(ABS_RY) 717 | BitAbs(ABS_RZ) 718 | BitAbs(ABS_THROTTLE) 719 | BitAbs(ABS_RUDDER) 720 | BitAbs(ABS_WHEEL) 721 | BitAbs(ABS_GAS) 722 | BitAbs(ABS_BRAKE) 723 | BitAbs(ABS_HAT0X) 724 | BitAbs(ABS_HAT0Y) 725 | BitAbs(ABS_HAT1X) 726 | BitAbs(ABS_HAT1Y) 727 | BitAbs(ABS_HAT2X) 728 | BitAbs(ABS_HAT2Y) 729 | BitAbs(ABS_HAT3X) 730 | BitAbs(ABS_HAT3Y) 731 | BitAbs(ABS_PRESSURE) 732 | BitAbs(ABS_DISTANCE) 733 | BitAbs(ABS_TILT_X) 734 | BitAbs(ABS_TILT_Y) 735 | BitAbs(ABS_TOOL_WIDTH) 736 | BitAbs(ABS_VOLUME) 737 | BitAbs(ABS_MISC) 738 | BitAbs(ABS_MT_SLOT) 739 | BitAbs(ABS_MT_TOUCH_MAJOR) 740 | BitAbs(ABS_MT_TOUCH_MINOR) 741 | BitAbs(ABS_MT_WIDTH_MAJOR) 742 | BitAbs(ABS_MT_WIDTH_MINOR) 743 | BitAbs(ABS_MT_ORIENTATION) 744 | BitAbs(ABS_MT_POSITION_X) 745 | BitAbs(ABS_MT_POSITION_Y) 746 | BitAbs(ABS_MT_TOOL_TYPE) 747 | BitAbs(ABS_MT_BLOB_ID) 748 | BitAbs(ABS_MT_TRACKING_ID) 749 | BitAbs(ABS_MT_PRESSURE) 750 | BitAbs(ABS_MT_DISTANCE) 751 | BitAbs(ABS_MT_TOOL_X) 752 | BitAbs(ABS_MT_TOOL_Y) 753 | EndAbs() 754 | 755 | BeginSw() 756 | BitSw(SW_LID) 757 | BitSw(SW_TABLET_MODE) 758 | BitSw(SW_HEADPHONE_INSERT) 759 | BitSw(SW_RFKILL_ALL) 760 | BitSw2(SW_RADIO, SW_RFKILL_ALL) 761 | BitSw(SW_MICROPHONE_INSERT) 762 | BitSw(SW_DOCK) 763 | BitSw(SW_LINEOUT_INSERT) 764 | BitSw(SW_JACK_PHYSICAL_INSERT) 765 | BitSw(SW_VIDEOOUT_INSERT) 766 | BitSw(SW_CAMERA_LENS_COVER) 767 | BitSw(SW_KEYPAD_SLIDE) 768 | BitSw(SW_FRONT_PROXIMITY) 769 | BitSw(SW_ROTATE_LOCK) 770 | BitSw(SW_LINEIN_INSERT) 771 | BitSw(SW_MUTE_DEVICE) 772 | BitSw(SW_PEN_INSERTED) 773 | EndSw() 774 | 775 | BeginMsc() 776 | BitMsc(MSC_SERIAL) 777 | BitMsc(MSC_PULSELED) 778 | BitMsc(MSC_GESTURE) 779 | BitMsc(MSC_RAW) 780 | BitMsc(MSC_SCAN) 781 | BitMsc(MSC_TIMESTAMP) 782 | EndMsc() 783 | 784 | BeginLed() 785 | BitLed(LED_NUML) 786 | BitLed(LED_CAPSL) 787 | BitLed(LED_SCROLLL) 788 | BitLed(LED_COMPOSE) 789 | BitLed(LED_KANA) 790 | BitLed(LED_SLEEP) 791 | BitLed(LED_SUSPEND) 792 | BitLed(LED_MUTE) 793 | BitLed(LED_MISC) 794 | BitLed(LED_MAIL) 795 | BitLed(LED_CHARGING) 796 | EndLed() 797 | 798 | BeginRep() 799 | BitRep(REP_DELAY) 800 | BitRep(REP_PERIOD) 801 | EndRep() 802 | 803 | BeginSnd() 804 | BitSnd(SND_CLICK) 805 | BitSnd(SND_BELL) 806 | BitSnd(SND_TONE) 807 | EndSnd() 808 | 809 | BeginFF() 810 | BitFF(FF_RUMBLE) 811 | BitFF(FF_PERIODIC) 812 | BitFF(FF_CONSTANT) 813 | BitFF(FF_SPRING) 814 | BitFF(FF_FRICTION) 815 | BitFF(FF_DAMPER) 816 | BitFF(FF_INERTIA) 817 | BitFF(FF_RAMP) 818 | BitFF(FF_SQUARE) 819 | BitFF(FF_TRIANGLE) 820 | BitFF(FF_SINE) 821 | BitFF(FF_SAW_UP) 822 | BitFF(FF_SAW_DOWN) 823 | BitFF(FF_CUSTOM) 824 | BitFF(FF_GAIN) 825 | BitFF(FF_AUTOCENTER) 826 | EndFF() 827 | -------------------------------------------------------------------------------- /examples/xbox.ini: -------------------------------------------------------------------------------- 1 | [steam] 2 | name=Steam 3 | mouse=N 4 | 5 | [variables] 6 | LPolar=rotate(polar(Steam.LPadX, Steam.LPadY), deg(18)) 7 | LHAT=Steam.LPadClick and get_radius($LPolar) > 0.4 8 | LHATAngle=65 9 | 10 | [output] 11 | name=Microsoft X-Box 360 pad 12 | bus=USB 13 | vendor=0x045e 14 | product=0x028e 15 | 16 | ABS_X = Steam.StickX 17 | ABS_Y = Steam.StickY 18 | ABS_RX = Steam.RPadX 19 | ABS_RY = Steam.RPadY 20 | ABS_Z = Steam.LTrigger 21 | ABS_RZ = Steam.RTrigger 22 | 23 | BTN_A = Steam.BtnA 24 | BTN_B = Steam.BtnB 25 | BTN_X = Steam.BtnX 26 | BTN_Y = Steam.BtnY 27 | 28 | BTN_TL = Steam.LBumper 29 | BTN_TR = Steam.RBumper 30 | BTN_SELECT = Steam.Menu 31 | BTN_START = Steam.Escape 32 | BTN_MODE = Steam.Logo 33 | 34 | BTN_THUMBL = Steam.Stick 35 | BTN_THUMBR = Steam.RPadClick 36 | 37 | ABS_HAT0X=($LHAT and \ 38 | (between_angle($LPolar, deg(-$LHATAngle), deg($LHATAngle)) - \ 39 | between_angle($LPolar, deg(180 - $LHATAngle), deg(-180 + $LHATAngle)))) 40 | ABS_HAT0Y=($LHAT and \ 41 | (between_angle($LPolar, deg(90 - $LHATAngle), deg(90 + $LHATAngle)) - \ 42 | between_angle($LPolar, deg(-90 - $LHATAngle), deg(-90 + $LHATAngle)))) 43 | -------------------------------------------------------------------------------- /examples/xbox2.ini: -------------------------------------------------------------------------------- 1 | [steam] 2 | name=Steam 3 | mouse=N 4 | 5 | [variables] 6 | quat=quaternion(Steam.LBack, Steam.QuatW, Steam.QuatX, Steam.QuatY, Steam.QuatZ) 7 | 8 | [output] 9 | name=Microsoft X-Box 360 pad 10 | bus=USB 11 | vendor=0x045e 12 | product=0x128e 13 | 14 | ABS_X = 1.25*Steam.StickX 15 | ABS_Y = 1.25*Steam.StickY 16 | ABS_RX = defuzz(1.25*Steam.RPadX, 0.05) or 2 * get_yaw($quat) 17 | ABS_RY = defuzz(1.25*Steam.RPadY, 0.05) or 2 * get_pitch($quat) 18 | ABS_Z = Steam.LTrigger 19 | ABS_RZ = Steam.RTrigger 20 | 21 | BTN_BASE = Steam.West 22 | BTN_BASE2 = Steam.East 23 | BTN_BASE3 = Steam.North 24 | BTN_BASE4 = Steam.South 25 | 26 | BTN_A = Steam.BtnA 27 | BTN_B = Steam.BtnB 28 | BTN_X = Steam.BtnY 29 | BTN_Y = Steam.BtnX 30 | 31 | BTN_TL = Steam.LBumper 32 | BTN_TR = toggle(Steam.LTriggerFull) 33 | BTN_TL2 = Steam.RBumper 34 | BTN_TR2 = Steam.RTriggerFull 35 | 36 | BTN_SELECT = Steam.Stick 37 | BTN_START = Steam.RPadClick 38 | BTN_MODE = Steam.Escape 39 | BTN_THUMBL = Steam.Menu 40 | -------------------------------------------------------------------------------- /inifile.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017, Rodrigo Rivas Costa 4 | 5 | This file is part of inputmap. 6 | 7 | inputmap is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | inputmap is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with inputmap. If not, see . 19 | 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include "inifile.h" 26 | 27 | 28 | std::string trim(const std::string &s) 29 | { 30 | auto wsfront=std::find_if_not(s.begin(),s.end(),[](int c){return std::isspace(c);}); 31 | auto wsback=std::find_if_not(s.rbegin(),s.rend(),[](int c){return std::isspace(c);}).base(); 32 | return (wsback<=wsfront ? std::string() : std::string(wsfront,wsback)); 33 | } 34 | 35 | void IniFile::Dump(std::ostream &os) 36 | { 37 | for (auto &s : *this) 38 | { 39 | os << "[" << s.m_name << "]\n"; 40 | for (auto &e : s.m_entries) 41 | { 42 | os << e.m_name << "=" << e.m_value << "\n"; 43 | } 44 | } 45 | } 46 | 47 | IniFile::IniFile(const std::string &fileName) 48 | { 49 | std::ifstream ifs(fileName); 50 | if (!ifs) 51 | throw std::runtime_error(fileName); 52 | load(ifs); 53 | } 54 | 55 | void IniFile::load(std::istream &is) 56 | { 57 | std::string line; 58 | while (is) 59 | { 60 | std::string line0; 61 | std::getline(is, line0); 62 | line += line0; 63 | if (!line.empty() && line[line.size() - 1] == '\\') 64 | { 65 | line.resize(line.size() - 1); 66 | continue; 67 | } 68 | std::string full = trim(line); 69 | line.clear(); 70 | 71 | if (full.empty() || full[0] == '#') 72 | continue; 73 | 74 | if (full[0] == '[') 75 | { 76 | if (full[full.size() - 1] != ']') 77 | throw std::runtime_error("wrong section name"); 78 | 79 | m_sections.push_back(IniSection()); 80 | m_sections.back().m_name = full.substr(1, full.size() - 2); 81 | } 82 | else 83 | { 84 | if (m_sections.empty()) 85 | throw std::runtime_error("item without section"); 86 | m_sections.back().add_line(full); 87 | } 88 | } 89 | } 90 | 91 | void IniSection::add_line(const std::string &line) 92 | { 93 | IniEntry entry; 94 | size_t eq = line.find('='); 95 | if (eq == std::string::npos) 96 | { 97 | entry.m_name = line; 98 | } 99 | else 100 | { 101 | entry.m_name = trim(line.substr(0, eq)); 102 | entry.m_value = trim(line.substr(eq + 1)); 103 | } 104 | m_entries.push_back(entry); 105 | } 106 | 107 | const IniSection *IniFile::find_single_section(const std::string &name) const 108 | { 109 | std::vector values = find_multi_section(name); 110 | if (values.size() > 1) 111 | throw std::runtime_error("multiple " + name); 112 | if (values.empty()) 113 | return nullptr; 114 | else 115 | return values.front(); 116 | } 117 | 118 | std::vector IniFile::find_multi_section(const std::string &name) const 119 | { 120 | std::vector res; 121 | for (auto &s : m_sections) 122 | { 123 | if (s.m_name == name) 124 | res.push_back(&s); 125 | } 126 | return res; 127 | } 128 | 129 | std::string IniSection::find_single_value(const std::string &name) const 130 | { 131 | std::vector values = find_multi_value(name); 132 | if (values.size() > 1) 133 | throw std::runtime_error("multiple " + name); 134 | if (values.empty()) 135 | return std::string(); 136 | else 137 | return values.front(); 138 | } 139 | 140 | std::vector IniSection::find_multi_value(const std::string &name) const 141 | { 142 | std::vector res; 143 | for (auto &e : m_entries) 144 | { 145 | if (e.m_name == name) 146 | res.push_back(e.m_value); 147 | } 148 | return res; 149 | } 150 | 151 | -------------------------------------------------------------------------------- /inifile.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017, Rodrigo Rivas Costa 4 | 5 | This file is part of inputmap. 6 | 7 | inputmap is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | inputmap is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with inputmap. If not, see . 19 | 20 | */ 21 | 22 | #ifndef INICONF_H_INCLUDED 23 | #define INICONF_H_INCLUDED 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | inline bool parse_bool(const std::string &txt, bool def) 30 | { 31 | if (txt.empty()) 32 | return def; 33 | return txt == "Y" || txt == "y" || txt == "1"; 34 | } 35 | 36 | inline int parse_int(const std::string &txt, int def) 37 | { 38 | if (txt.empty()) 39 | return def; 40 | std::istringstream ifs(txt); 41 | ifs >> def; 42 | return def; 43 | } 44 | 45 | inline int parse_hex_int(const std::string &txt, int def) 46 | { 47 | if (txt.empty()) 48 | return def; 49 | std::istringstream ifs(txt); 50 | ifs >> std::hex >> def; 51 | return def; 52 | } 53 | 54 | std::string trim(const std::string &s); 55 | 56 | class IniEntry 57 | { 58 | friend class IniFile; 59 | friend class IniSection; 60 | public: 61 | template 62 | void preprocess_value(F filter) 63 | { 64 | m_value = filter(std::move(m_value)); 65 | } 66 | const std::string name() const 67 | { return m_name; } 68 | const std::string value() const 69 | { return m_value; } 70 | private: 71 | std::string m_name; 72 | std::string m_value; 73 | }; 74 | 75 | class IniSection 76 | { 77 | friend class IniFile; 78 | public: 79 | const std::string name() const 80 | { return m_name; } 81 | template 82 | void preprocess_values(F filter) 83 | { 84 | for (auto &v : m_entries) 85 | v.preprocess_value(filter); 86 | } 87 | std::string find_single_value(const std::string &name) const; 88 | std::vector find_multi_value(const std::string &name) const; 89 | 90 | typedef std::vector::const_iterator entry_iterator; 91 | entry_iterator begin() const 92 | { return m_entries.begin(); } 93 | entry_iterator end() const 94 | { return m_entries.end(); } 95 | private: 96 | std::string m_name; 97 | std::vector m_entries; 98 | 99 | void add_line(const std::string &line); 100 | }; 101 | 102 | class IniFile 103 | { 104 | public: 105 | IniFile(const std::string &fileName); 106 | template 107 | void preprocess_values(F filter) 108 | { 109 | for (auto &s : m_sections) 110 | s.preprocess_values(filter); 111 | } 112 | void Dump(std::ostream &os); 113 | 114 | const IniSection *find_single_section(const std::string &name) const; 115 | std::vector find_multi_section(const std::string &name) const; 116 | 117 | std::vector::const_iterator begin() const 118 | { return m_sections.begin(); } 119 | std::vector::const_iterator end() const 120 | { return m_sections.end(); } 121 | private: 122 | std::vector m_sections; 123 | void load(std::istream &is); 124 | }; 125 | 126 | #endif /* INICONF_H_INCLUDED */ 127 | -------------------------------------------------------------------------------- /inputdev.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017, Rodrigo Rivas Costa 4 | 5 | This file is part of inputmap. 6 | 7 | inputmap is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | inputmap is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with inputmap. If not, see . 19 | 20 | */ 21 | 22 | #include 23 | #include "inputdev.h" 24 | #include "event-codes.h" 25 | 26 | InputDevice::InputDevice(const IniSection &ini) 27 | { 28 | m_name = ini.find_single_value("name"); 29 | if (m_name.empty()) 30 | throw std::runtime_error("input without name"); 31 | } 32 | 33 | InputDeviceEvent::InputDeviceEvent(const IniSection &ini, FD the_fd) 34 | :InputDevice(ini), m_fd(std::move(the_fd)), m_num_evs(0) 35 | { 36 | bool grab = parse_bool(ini.find_single_value("grab"), false); 37 | 38 | if (grab) 39 | test(ioctl(fd(), EVIOCGRAB, 1), "EVIOCGRAB"); 40 | 41 | char buf[1024] = ""; 42 | input_id iid; 43 | if (ioctl(fd(), EVIOCGID, &iid) >= 0) 44 | printf(" iid=%d %04x:%04x %d\n", iid.bustype, iid.vendor, iid.product, iid.version); 45 | if (ioctl(fd(), EVIOCGNAME(sizeof(buf)), buf) >= 0) 46 | printf(" name='%s'\n", buf); 47 | if (ioctl(fd(), EVIOCGPHYS(sizeof(buf)), buf) >= 0) 48 | printf(" phys='%s'\n", buf); 49 | if (ioctl(fd(), EVIOCGUNIQ(sizeof(buf)), buf) >=0) 50 | printf(" uniq='%s'\n", buf); 51 | if (ioctl(fd(), EVIOCGPROP(sizeof(buf)), buf) >= 0) 52 | printf(" prop='%s'\n", buf); 53 | 54 | test(ioctl(fd(), EVIOCGBIT(EV_REL, sizeof(buf)), buf), "EV_REL"); 55 | printf(" rel: "); 56 | for (const auto &kv : g_rel_names) 57 | { 58 | if (!kv.name) 59 | continue; 60 | if (test_bit(kv.id, (unsigned char*)buf)) 61 | printf(" %s", kv.name); 62 | } 63 | printf("\n"); 64 | 65 | test(ioctl(fd(), EVIOCGBIT(EV_ABS, sizeof(buf)), buf), "EV_ABS"); 66 | printf(" abs: "); 67 | for (const auto &kv : g_abs_names) 68 | { 69 | if (!kv.name) 70 | continue; 71 | if (test_bit(kv.id, (unsigned char*)buf)) 72 | { 73 | printf(" %s", kv.name); 74 | test(ioctl(fd(), EVIOCGABS(kv.id), &m_status.absinfo[kv.id]), "EVIOCGABS"); 75 | } 76 | } 77 | printf("\n"); 78 | 79 | test(ioctl(fd(), EVIOCGBIT(EV_KEY, sizeof(buf)), buf), "EV_KEY"); 80 | printf(" key: "); 81 | for (const auto &kv : g_key_names) 82 | { 83 | if (!kv.name) 84 | continue; 85 | if (test_bit(kv.id, (unsigned char*)buf)) 86 | printf(" %s", kv.name); 87 | } 88 | printf("\n"); 89 | } 90 | 91 | ValueId InputDeviceEvent::parse_value(const std::string &name) 92 | { 93 | for (const auto &kv : g_key_names) 94 | { 95 | if (kv.name && kv.name == name) 96 | return ValueId(EV_KEY, kv.id); 97 | } 98 | for (const auto &kv : g_rel_names) 99 | { 100 | if (kv.name && kv.name == name) 101 | return ValueId(EV_REL, kv.id); 102 | } 103 | for (const auto &kv : g_abs_names) 104 | { 105 | if (kv.name && kv.name == name) 106 | return ValueId(EV_ABS, kv.id); 107 | } 108 | for (const auto &kv : g_ff_names) 109 | { 110 | if (kv.name && kv.name == name) 111 | return ValueId(EV_FF, kv.id); 112 | } 113 | throw std::runtime_error("unknown value name " + name); 114 | } 115 | 116 | PollResult InputDeviceEvent::on_poll(int event) 117 | { 118 | if ((event & EPOLLIN) == 0) 119 | return PollResult::None; 120 | 121 | int free_evs = countof(m_evs) - m_num_evs; 122 | if (free_evs == 0) 123 | throw std::runtime_error("input buffer overflow"); 124 | int res = read(fd(), &m_evs[m_num_evs], free_evs * sizeof(input_event)); 125 | if (res == -1) 126 | { 127 | if (errno == EINTR) 128 | return PollResult::None; 129 | perror("input read"); 130 | return PollResult::Error; 131 | } 132 | 133 | m_num_evs += res / sizeof(input_event); 134 | if (m_evs[m_num_evs - 1].type != EV_SYN) 135 | { 136 | printf("no EV_SYN, buffering\n"); 137 | return PollResult::None; 138 | } 139 | 140 | for (int i = 0; i < m_num_evs; ++i) 141 | on_input(m_evs[i]); 142 | m_num_evs = 0; 143 | return PollResult::Sync; 144 | } 145 | 146 | void InputDeviceEvent::on_input(input_event &ev) 147 | { 148 | switch (ev.type) 149 | { 150 | case EV_SYN: 151 | //printf("SYN %d %d\n", ev.code, ev.value); 152 | break; 153 | case EV_ABS: 154 | //printf("ABS %d %d\n", ev.code, ev.value); 155 | m_status.abs[ev.code] = ev.value; 156 | break; 157 | case EV_REL: 158 | //printf("REL %d %d\n", ev.code, ev.value); 159 | m_status.rel[ev.code] += ev.value; 160 | break; 161 | case EV_KEY: 162 | //printf("KEY %d %d\n", ev.code, ev.value); 163 | m_status.key[ev.code] = ev.value; 164 | break; 165 | case EV_MSC: 166 | //printf("MSC %d %d\n", ev.code, ev.value); 167 | break; 168 | default: 169 | //printf("??? %d %d %d\n", ev.type, ev.code, ev.value); 170 | break; 171 | } 172 | } 173 | 174 | value_t InputDeviceEvent::get_value(const ValueId &id) 175 | { 176 | switch (id.type) 177 | { 178 | case EV_REL: 179 | return m_status.rel[id.code]; 180 | case EV_KEY: 181 | return m_status.key[id.code]; 182 | case EV_ABS: 183 | { 184 | value_t x = m_status.abs[id.code]; 185 | value_t max = m_status.absinfo[id.code].maximum, min = m_status.absinfo[id.code].minimum; 186 | return 1 + 2 * (x - max) / (max - min); 187 | } 188 | default: 189 | return 0; 190 | } 191 | } 192 | 193 | void InputDeviceEvent::flush() 194 | { 195 | memset(m_status.rel, 0, sizeof(m_status.rel)); 196 | } 197 | 198 | int InputDeviceEvent::ff_upload(const ff_effect &eff) 199 | { 200 | ff_effect ff = eff; 201 | ff.id = -1; 202 | int res = ioctl(fd(), EVIOCSFF, &ff); 203 | if (res < 0) 204 | return -errno; 205 | return ff.id; 206 | } 207 | 208 | int InputDeviceEvent::ff_erase(int id) 209 | { 210 | int res = ioctl(fd(), EVIOCRMFF, id); 211 | if (res < 0) 212 | return -errno; 213 | return 0; 214 | } 215 | 216 | void InputDeviceEvent::ff_run(int eff, bool on) 217 | { 218 | input_event ev{}; 219 | ev.type = EV_FF; 220 | ev.code = eff; 221 | ev.value = on? 1 : 0; 222 | test(write(fd(), &ev, sizeof(ev)), "write ff"); 223 | } 224 | 225 | std::shared_ptr InputDeviceEventCreate(const IniSection &ini, FD fd) 226 | { 227 | return std::make_shared(ini, std::move(fd)); 228 | } 229 | 230 | -------------------------------------------------------------------------------- /inputdev.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017, Rodrigo Rivas Costa 4 | 5 | This file is part of inputmap. 6 | 7 | inputmap is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | inputmap is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with inputmap. If not, see . 19 | 20 | */ 21 | 22 | #ifndef INPUTDEV_H_INCLUDED 23 | #define INPUTDEV_H_INCLUDED 24 | 25 | #include 26 | #include 27 | #include "steam/fd.h" 28 | #include "inifile.h" 29 | 30 | int bus_id(const char *bus_name); 31 | 32 | struct InputStatus 33 | { 34 | int abs[ABS_CNT]; 35 | int rel[REL_CNT]; 36 | bool key[KEY_CNT]; 37 | input_absinfo absinfo[ABS_CNT]; 38 | 39 | InputStatus() 40 | { 41 | reset(); 42 | } 43 | void reset() 44 | { 45 | memset(abs, 0, sizeof(abs)); 46 | memset(rel, 0, sizeof(rel)); 47 | memset(key, 0, sizeof(key)); 48 | } 49 | }; 50 | 51 | struct ValueId 52 | { 53 | int type; 54 | int code; 55 | 56 | ValueId() 57 | :type(0), code(0) 58 | {} 59 | ValueId(int t, int c) 60 | :type(t), code(c) 61 | {} 62 | }; 63 | 64 | class InputDevice; 65 | 66 | enum class PollResult 67 | { 68 | None, 69 | Error, 70 | Sync, 71 | }; 72 | 73 | struct IPollable 74 | { 75 | virtual ~IPollable() {} 76 | virtual int fd() =0; 77 | virtual PollResult on_poll(int event) =0; 78 | }; 79 | 80 | typedef float value_t; 81 | 82 | class InputDevice : public std::enable_shared_from_this, 83 | public IPollable 84 | { 85 | public: 86 | const std::string &name() const noexcept 87 | { return m_name; } 88 | 89 | virtual ValueId parse_value(const std::string &name) =0; 90 | virtual value_t get_value(const ValueId &id) =0; 91 | virtual int ff_upload(const ff_effect &eff) =0; 92 | virtual int ff_erase(int id) =0; 93 | virtual void ff_run(int eff, bool on) =0; 94 | virtual void flush() =0; 95 | 96 | protected: 97 | InputDevice(const IniSection &ini); 98 | private: 99 | std::string m_name; 100 | }; 101 | 102 | std::shared_ptr InputDeviceEventCreate(const IniSection &ini, FD fd); 103 | 104 | class InputDeviceEvent : public InputDevice 105 | { 106 | public: 107 | explicit InputDeviceEvent(const IniSection &ini, FD fd); 108 | 109 | virtual int fd() 110 | { return m_fd.get(); } 111 | virtual ValueId parse_value(const std::string &name); 112 | virtual PollResult on_poll(int event); 113 | virtual value_t get_value(const ValueId &id); 114 | virtual int ff_upload(const ff_effect &eff); 115 | virtual int ff_erase(int id); 116 | virtual void ff_run(int eff, bool on); 117 | virtual void flush(); 118 | private: 119 | FD m_fd; 120 | input_event m_evs[128]; 121 | int m_num_evs; 122 | InputStatus m_status; 123 | 124 | void on_input(input_event &ev); 125 | }; 126 | 127 | #endif /* INPUTDEV_H_INCLUDED */ 128 | -------------------------------------------------------------------------------- /inputmap.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017, Rodrigo Rivas Costa 4 | 5 | This file is part of inputmap. 6 | 7 | inputmap is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | inputmap is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with inputmap. If not, see . 19 | 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "inifile.h" 38 | #include "inputsteam.h" 39 | #include "outputdev.h" 40 | #include "steam/udev-wrapper.h" 41 | #include "steam/fd.h" 42 | #include "steam/steamcontroller.h" 43 | 44 | 45 | bool g_verbose = false; 46 | bool g_daemonize = false; 47 | const char *g_writepid; 48 | 49 | void help(const char *name) 50 | { 51 | printf("Usage %s file.ini\n\n", name); 52 | printf("Available options:\n"); 53 | printf("\t-v: Verbose output.\n"); 54 | printf("\t-d: Run in background (daemonize) when all output devices have been created.\n"); 55 | printf("\t-p : Write the PID into the given file. Useful to kill the program later.\n"); 56 | printf("\t-m =: Define a macro to be replaced in the ini file: {} will be replaced with .\n"); 57 | exit(EXIT_FAILURE); 58 | } 59 | 60 | volatile bool g_exit = false; 61 | 62 | template 63 | class InputFinder : public IInputByName 64 | { 65 | public: 66 | InputFinder(IT begin, IT end, std::map &variables) 67 | :m_begin(begin), m_end(end), m_variables(variables) 68 | { 69 | } 70 | std::shared_ptr find_input(const std::string &name) override 71 | { 72 | auto it = std::find_if(m_begin, m_end, [&name](std::shared_ptr &x) { return x->name() == name; }); 73 | if (it != m_end) 74 | return *it; 75 | return std::shared_ptr(); 76 | } 77 | Variable *find_variable(const std::string &name) override 78 | { 79 | auto it = m_variables.find(name); 80 | if (it == m_variables.end()) 81 | return nullptr; 82 | return &it->second; 83 | } 84 | private: 85 | IT m_begin, m_end; 86 | std::map &m_variables; 87 | }; 88 | 89 | struct 90 | { 91 | const char *name; 92 | int id; 93 | } g_buses[] = 94 | { 95 | {"usb", BUS_USB}, 96 | {"pci", BUS_PCI}, 97 | {"isapnp", BUS_ISAPNP}, 98 | {"usb", BUS_USB}, 99 | {"hil", BUS_HIL}, 100 | {"bluetooth", BUS_BLUETOOTH}, 101 | {"virtual", BUS_VIRTUAL}, 102 | {"isa", BUS_ISA}, 103 | {"i8042", BUS_I8042}, 104 | {"xtkbd", BUS_XTKBD}, 105 | {"rs232", BUS_RS232}, 106 | {"gameport", BUS_GAMEPORT}, 107 | {"parport", BUS_PARPORT}, 108 | {"amiga", BUS_AMIGA}, 109 | {"adb", BUS_ADB}, 110 | {"i2c", BUS_I2C}, 111 | {"host", BUS_HOST}, 112 | {"gsc", BUS_GSC}, 113 | {"atari", BUS_ATARI}, 114 | {"spi", BUS_SPI}, 115 | {"rmi", BUS_RMI}, 116 | {"cec", BUS_CEC}, 117 | }; 118 | 119 | const char *bus_name(int bus_id) 120 | { 121 | for (auto &bus: g_buses) 122 | { 123 | if (bus.id == bus_id) 124 | return bus.name; 125 | } 126 | return "(null)"; 127 | } 128 | 129 | int bus_id(const char *bus_name) 130 | { 131 | for (auto &bus: g_buses) 132 | { 133 | if (strcasecmp(bus.name, bus_name) == 0) 134 | return bus.id; 135 | } 136 | throw std::runtime_error(std::string("unknown bus name: ") + bus_name); 137 | } 138 | 139 | struct FoundInputDevice 140 | { 141 | FD fd; 142 | std::string dev, name, uniq; 143 | input_id iid; 144 | }; 145 | 146 | std::vector list_input_devices() 147 | { 148 | std::vector res; 149 | udev_ptr ud { udev_new() }; 150 | auto udevs = find_udev_devices(ud.get(), nullptr, "input", nullptr, nullptr); 151 | if (g_verbose) 152 | printf("Looking for input devices...\n"); 153 | for (auto udev : udevs) 154 | { 155 | udev_device_ptr dx { udev_device_new_from_syspath(ud.get(), udev.c_str()) }; 156 | const char *dev = udev_device_get_devnode(dx.get()); 157 | 158 | if (!dev) 159 | continue; 160 | auto slash = udev.rfind('/'); 161 | if (slash == std::string::npos) 162 | continue; 163 | if (udev.substr(slash + 1, 5) != "event") 164 | continue; 165 | 166 | FD fd { open(dev, O_RDONLY) }; 167 | if (!fd) 168 | { 169 | if (g_verbose) 170 | fprintf(stderr, "%s: %s\n", dev, strerror(errno)); 171 | continue; 172 | } 173 | 174 | FoundInputDevice fid; 175 | fid.dev = dev; 176 | if (ioctl(fd.get(), EVIOCGID, &fid.iid) < 0) 177 | { 178 | if (g_verbose) 179 | fprintf(stderr, "%s: %s\n", dev, strerror(errno)); 180 | continue; 181 | } 182 | 183 | char buf[1024]; 184 | //do not fail if the device has no name 185 | if (ioctl(fd.get(), EVIOCGNAME(sizeof(buf)), buf) >= 0) 186 | fid.name = trim(buf); 187 | if (ioctl(fd.get(), EVIOCGUNIQ(sizeof(buf)), buf) >= 0) 188 | fid.uniq = trim(buf); 189 | 190 | if (g_verbose) 191 | { 192 | std::string extra; 193 | if (!fid.uniq.empty()) 194 | extra = " : <" + fid.uniq + ">"; 195 | printf("%04x:%04x %5s %s '%s'%s\n", fid.iid.vendor, fid.iid.product, bus_name(fid.iid.bustype), dev, fid.name.c_str(), extra.c_str()); 196 | } 197 | fid.fd = std::move(fd); 198 | res.push_back(std::move(fid)); 199 | } 200 | return res; 201 | } 202 | 203 | FoundInputDevice *find_input_device(std::vector &fids, int bus, const std::string &vp) 204 | { 205 | auto colon = vp.find(':'); 206 | if (colon == std::string::npos) 207 | throw std::runtime_error("invalid vendor:product pair: " + vp); 208 | 209 | int vendor = parse_hex_int(vp.substr(0, colon), -1); 210 | int product = parse_hex_int(vp.substr(colon + 1), -1); 211 | if (vendor == -1 || product == -1) 212 | throw std::runtime_error("invalid vendor:product pair: " + vp); 213 | 214 | for (auto &fid: fids) 215 | { 216 | if (bus == fid.iid.bustype && vendor == fid.iid.vendor && product == fid.iid.product) 217 | return &fid; 218 | } 219 | throw std::runtime_error("device " + vp + " not found"); 220 | } 221 | 222 | FD find_input_device_from_section(std::vector &fids, const IniSection *s) 223 | { 224 | std::string sdev = s->find_single_value("dev"); 225 | if (!sdev.empty()) 226 | return FD { open(sdev.c_str(), O_RDONLY) }; 227 | 228 | std::string sbyid = s->find_single_value("by-id"); 229 | if (!sbyid.empty()) 230 | return FD { open(("/dev/input/by-id/" + sbyid).c_str(), O_RDONLY) }; 231 | 232 | std::string sbypath = s->find_single_value("by-path"); 233 | if (!sbypath.empty()) 234 | return FD { open(("/dev/input/by-path/" + sbypath).c_str(), O_RDONLY) }; 235 | 236 | std::string sbyuniq = s->find_single_value("by-uniq"); 237 | if (!sbyuniq.empty()) 238 | { 239 | for (auto &fid: fids) 240 | { 241 | if (fid.uniq == sbyuniq) 242 | return std::move(fid.fd); 243 | } 244 | throw std::runtime_error("uniq device '" + sbyuniq + "' not found"); 245 | } 246 | 247 | std::string sbyname = s->find_single_value("by-name"); 248 | if (!sbyname.empty()) 249 | { 250 | for (auto &fid: fids) 251 | { 252 | if (fid.name == sbyname) 253 | return std::move(fid.fd); 254 | } 255 | throw std::runtime_error("name device '" + sbyname + "' not found"); 256 | } 257 | 258 | for (const auto &bus : g_buses) 259 | { 260 | std::string sbus = s->find_single_value(bus.name); 261 | if (!sbus.empty()) 262 | { 263 | FoundInputDevice *fid = find_input_device(fids, bus.id, sbus); 264 | return std::move(fid->fd); 265 | } 266 | } 267 | 268 | throw std::runtime_error("input section without device: " + s->name()); 269 | } 270 | 271 | int main2(int argc, char **argv) 272 | { 273 | int opt; 274 | std::map defines; 275 | while ((opt = getopt(argc, argv, "vdp:m:")) != -1) 276 | { 277 | switch (opt) 278 | { 279 | case 'v': 280 | g_verbose = true; 281 | break; 282 | case 'd': 283 | g_daemonize = true; 284 | break; 285 | case 'p': 286 | g_writepid = optarg; 287 | break; 288 | case 'm': 289 | { 290 | std::string arg(optarg); 291 | auto eq = arg.find('='); 292 | if (eq == std::string::npos) 293 | { 294 | fprintf(stderr, "%s error: argument to '-a' must have an '='\n", argv[0]); 295 | exit(1); 296 | } 297 | std::string name = arg.substr(0, eq); 298 | std::string value = arg.substr(eq + 1); 299 | defines[name] = value; 300 | } 301 | break; 302 | 303 | default: 304 | help(argv[0]); 305 | } 306 | } 307 | 308 | if (optind + 1 != argc) 309 | { 310 | //if run with -v but without a .ini file, dump the device list instead of the help 311 | if (g_verbose) 312 | { 313 | list_input_devices(); 314 | exit(1); 315 | } 316 | help(argv[0]); 317 | } 318 | 319 | std::string name = argv[optind]; 320 | IniFile ini(name); 321 | if (!defines.empty()) 322 | { 323 | ini.preprocess_values([&defines](std::string v) 324 | { 325 | auto vv = v; 326 | size_t start, end; 327 | start = v.find('{'); 328 | while (start != std::string::npos) 329 | { 330 | end = v.find('}', start + 1); 331 | if (end == std::string::npos) 332 | break; 333 | std::string name = v.substr(start + 1, end - start - 1); 334 | auto it = defines.find(name); 335 | if (it == defines.end()) 336 | { 337 | fprintf(stderr, "Warning: macro '%s' used but not defined\n", name.c_str()); 338 | } 339 | else 340 | { 341 | v = v.substr(0, start) + it->second + v.substr(end + 1); 342 | end += it->second.size() - name.size() - 2; //2 for the '{' and '}' 343 | } 344 | start = v.find('{', end + 1); 345 | } 346 | return v; 347 | }); 348 | } 349 | //ini.Dump(std::cout); 350 | 351 | std::list> inputs; 352 | std::list outputs; 353 | 354 | //NOTE: close() an input device may take quite some time, so closing the full list of devices 355 | //may add up to 1 second (that is 1000 ms!). Not a big deal unless you are waiting for the program 356 | //to start. The important thing anyone may be waiting for is the creation of the output devices, so we 357 | //delay the closing of the input devices until the output devices are created. 358 | std::vector fids = list_input_devices(); 359 | 360 | for (auto &s : ini.find_multi_section("steam")) 361 | { 362 | auto dev = std::make_shared(*s); 363 | inputs.push_back(dev); 364 | } 365 | for (auto &s : ini.find_multi_section("input")) 366 | { 367 | FD fd = find_input_device_from_section(fids, s); 368 | if (!fd) 369 | throw std::runtime_error("input device alreay in use: " + s->find_single_value("name")); 370 | //printf("dev='%s'\n", dev.name.c_str()); 371 | auto dev = InputDeviceEventCreate(*s, std::move(fd)); 372 | inputs.push_back(dev); 373 | } 374 | 375 | std::map variables; 376 | 377 | InputFinder inputFinder(inputs.begin(), inputs.end(), variables); 378 | 379 | if (const IniSection *vars = ini.find_single_section("variables")) 380 | { 381 | for (auto &entry : *vars) 382 | { 383 | std::unique_ptr exp = parse_ref(entry.value(), inputFinder); 384 | Variable var(std::move(exp)); 385 | var.evaluate(); //to get the right default value, particularly for constants 386 | variables.emplace(entry.name(), std::move(var)); 387 | } 388 | } 389 | 390 | for (auto &s : ini.find_multi_section("output")) 391 | { 392 | std::string id = s->find_single_value("name"); 393 | printf("name='%s'\n", id.c_str()); 394 | outputs.emplace_back(*s, inputFinder); 395 | } 396 | 397 | //Output devices are already created so now we can close the unused input devices (see note above). 398 | fids.clear(); 399 | 400 | if (inputs.empty()) 401 | { 402 | fprintf(stderr, "warning: no inputs"); 403 | } 404 | if (outputs.empty()) 405 | { 406 | fprintf(stderr, "warning: no outputs"); 407 | } 408 | 409 | if (g_daemonize) 410 | { 411 | if (daemon(/*nochdir*/1, /*noclose*/1) < 0) 412 | perror("daemon"); 413 | } 414 | if (g_writepid) 415 | { 416 | std::ofstream ofs(g_writepid); 417 | ofs << getpid(); 418 | } 419 | 420 | FD epoll_fd { epoll_create1(0) }; 421 | 422 | for (auto &input : inputs) 423 | { 424 | epoll_event ev; 425 | ev.events = EPOLLIN; 426 | ev.data.ptr = static_cast(input.get()); 427 | test(epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, input->fd(), &ev), "EPOLL_CTL_ADD"); 428 | } 429 | for (auto &output : outputs) 430 | { 431 | epoll_event ev; 432 | ev.events = EPOLLIN; 433 | ev.data.ptr = static_cast(&output); 434 | test(epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, output.fd(), &ev), "EPOLL_CTL_ADD"); 435 | } 436 | 437 | nice(-10); 438 | 439 | //Drop privileges if run as a root set-user-id 440 | setgid(getgid()); 441 | setuid(getuid()); 442 | if (getuid() == 0) //still root? maybe used sudo or su 443 | { //then drop to nobody, if possible 444 | char buf[1024]; 445 | passwd pwd, *nobody; 446 | getpwnam_r("nobody", &pwd, buf, sizeof(buf), &nobody); 447 | if (nobody) 448 | { 449 | setgid(nobody->pw_gid); 450 | setuid(nobody->pw_uid); 451 | } 452 | else 453 | { 454 | fprintf(stderr, "Warning! nobody user not found, still running as root\n"); 455 | } 456 | } 457 | 458 | while (!g_exit) 459 | { 460 | epoll_event epoll_evs[1]; 461 | int res = epoll_wait(epoll_fd.get(), epoll_evs, countof(epoll_evs), -1); 462 | if (res == -1) 463 | { 464 | if (errno == EINTR) 465 | continue; 466 | perror("epoll"); 467 | exit(EXIT_FAILURE); 468 | } 469 | 470 | std::vector> deletes, synced; 471 | for (int i = 0; i < res; ++i) 472 | { 473 | epoll_event &ev = epoll_evs[i]; 474 | auto pollable = static_cast(ev.data.ptr); 475 | if (ev.events & EPOLLERR) 476 | { 477 | if (auto input = dynamic_cast(pollable)) 478 | deletes.push_back(input->shared_from_this()); 479 | continue; 480 | } 481 | auto res = pollable->on_poll(ev.events); 482 | switch (res) 483 | { 484 | case PollResult::None: 485 | break; 486 | case PollResult::Error: 487 | if (auto input = dynamic_cast(pollable)) 488 | deletes.push_back(input->shared_from_this()); 489 | break; 490 | case PollResult::Sync: 491 | if (auto input = dynamic_cast(pollable)) 492 | synced.push_back(input->shared_from_this()); 493 | break; 494 | } 495 | } 496 | for (auto &d : deletes) 497 | { 498 | inputs.remove(d); 499 | g_exit = true; 500 | } 501 | 502 | for (auto &v : variables) 503 | v.second.evaluate(); 504 | 505 | for (auto &d : outputs) 506 | d.sync(); 507 | for (auto &d : synced) 508 | d->flush(); 509 | } 510 | printf("Exiting...\n"); 511 | return EXIT_SUCCESS; 512 | } 513 | 514 | int main(int argc, char **argv) 515 | { 516 | struct sigaction sac {}; 517 | sac.sa_handler = [](int signo) { g_exit = true; }; 518 | sigaction(SIGINT, &sac, nullptr); 519 | sigaction(SIGHUP, &sac, nullptr); 520 | sigaction(SIGTERM, &sac, nullptr); 521 | 522 | try 523 | { 524 | return main2(argc, argv); 525 | } 526 | catch (std::exception &e) 527 | { 528 | fprintf(stderr, "\n *** Fatal error: %s\n", e.what()); 529 | return EXIT_FAILURE; 530 | } 531 | } 532 | -------------------------------------------------------------------------------- /inputsteam.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017, Rodrigo Rivas Costa 4 | 5 | This file is part of inputmap. 6 | 7 | inputmap is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | inputmap is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with inputmap. If not, see . 19 | 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "inputdev.h" 27 | #include "inputsteam.h" 28 | #include "event-codes.h" 29 | 30 | static EventName g_steam_abs_names[] = 31 | { 32 | {SteamAxis::X, "X"}, 33 | {SteamAxis::Y, "Y"}, 34 | {SteamAxis::StickX, "StickX"}, 35 | {SteamAxis::StickY, "StickY"}, 36 | {SteamAxis::LPadX, "LPadX"}, 37 | {SteamAxis::LPadY, "LPadY"}, 38 | {SteamAxis::RPadX, "RPadX"}, 39 | {SteamAxis::RPadY, "RPadY"}, 40 | {SteamAxis::LTrigger, "LTrigger"}, 41 | {SteamAxis::RTrigger, "RTrigger"}, 42 | {SteamAxis::GyroX, "GyroX"}, 43 | {SteamAxis::GyroY, "GyroY"}, 44 | {SteamAxis::GyroZ, "GyroZ"}, 45 | {SteamAxis::QuatW, "QuatW"}, 46 | {SteamAxis::QuatX, "QuatX"}, 47 | {SteamAxis::QuatY, "QuatY"}, 48 | {SteamAxis::QuatZ, "QuatZ"}, 49 | }; 50 | 51 | static EventName g_steam_button_names[] = 52 | { 53 | {SteamButton::RTriggerFull, "RTriggerFull"}, 54 | {SteamButton::LTriggerFull, "LTriggerFull"}, 55 | {SteamButton::RBumper, "RBumper"}, 56 | {SteamButton::LBumper, "LBumper"}, 57 | {SteamButton::BtnY, "BtnY"}, 58 | {SteamButton::BtnB, "BtnB"}, 59 | {SteamButton::BtnX, "BtnX"}, 60 | {SteamButton::BtnA, "BtnA"}, 61 | {SteamButton::North, "North"}, 62 | {SteamButton::East, "East"}, 63 | {SteamButton::West, "West"}, 64 | {SteamButton::South, "South"}, 65 | {SteamButton::Menu, "Menu"}, 66 | {SteamButton::Logo, "Logo"}, 67 | {SteamButton::Escape, "Escape"}, 68 | {SteamButton::LBack, "LBack"}, 69 | {SteamButton::RBack, "RBack"}, 70 | {SteamButton::LPadClick, "LPadClick"}, 71 | {SteamButton::RPadClick, "RPadClick"}, 72 | {SteamButton::LPadTouch, "LPadTouch"}, 73 | {SteamButton::RPadTouch, "RPadTouch"}, 74 | {SteamButton::Unknown2, "Unknown2"}, 75 | {SteamButton::Stick, "Stick"}, 76 | {SteamButton::LPadAndJoy, "LPadAndJoy"}, 77 | }; 78 | 79 | InputDeviceSteam::InputDeviceSteam(const IniSection &ini) 80 | :InputDevice(ini), 81 | m_steam(SteamController::Create(ini.find_single_value("serial").c_str())) 82 | { 83 | bool mouse = parse_bool(ini.find_single_value("mouse"), false); 84 | m_steam.set_emulation_mode(mouse? SteamEmulation::Mouse : SteamEmulation::None); 85 | 86 | std::string auto_haptic = ini.find_single_value("auto_haptic"); 87 | m_auto_haptic_left = auto_haptic.find('l') != std::string::npos || 88 | auto_haptic.find('L') != std::string::npos; 89 | m_auto_haptic_right = auto_haptic.find('r') != std::string::npos || 90 | auto_haptic.find('R') != std::string::npos; 91 | } 92 | 93 | ValueId InputDeviceSteam::parse_value(const std::string &name) 94 | { 95 | for (const auto &ev : g_steam_abs_names) 96 | { 97 | if (ev.name == name) 98 | { 99 | if (ev.id >= GyroX && ev.id <= QuatZ) 100 | { 101 | if (!m_accel_enabled) 102 | { 103 | m_accel_enabled = true; 104 | m_steam.set_accelerometer(true); 105 | } 106 | } 107 | return ValueId{ EV_ABS, ev.id }; 108 | } 109 | } 110 | for (const auto &ev : g_steam_button_names) 111 | { 112 | if (ev.name == name) 113 | return ValueId{ EV_KEY, ev.id }; 114 | } 115 | if (name == "Rumble") 116 | return ValueId(EV_FF, FF_RUMBLE); 117 | throw std::runtime_error("unknown value name " + name); 118 | } 119 | 120 | PollResult InputDeviceSteam::on_poll(int event) 121 | { 122 | if (!m_steam.on_poll(event)) 123 | return PollResult::None; 124 | 125 | if (m_auto_haptic_left) 126 | if (m_steam.get_button(SteamButton::LPadTouch)) 127 | m_steam.haptic_freq(true, 200, 50, 8000); 128 | if (m_auto_haptic_right) 129 | if (m_steam.get_button(SteamButton::RPadTouch)) 130 | m_steam.haptic_freq(false, 200, 50, 10000); 131 | return PollResult::Sync; 132 | } 133 | 134 | value_t InputDeviceSteam::get_value(const ValueId &id) 135 | { 136 | /* 137 | if (btn & SteamButton::RightPadTouch) 138 | send_cmd({0x8f, 0x08, 0x00, 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00}); 139 | if (btn & SteamButton::LeftPadTouch) 140 | send_cmd({0x8f, 0x08, 0x01, 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00}); 141 | */ 142 | 143 | switch (id.type) 144 | { 145 | case EV_ABS: 146 | //For some reason the pads are rotated by about 15 degrees 147 | switch (id.code) 148 | { 149 | case SteamAxis::LTrigger: 150 | case SteamAxis::RTrigger: 151 | //Should idle be 0 or -1? Currently it is 0 152 | return m_steam.get_axis(static_cast(id.code)) / 255.0F; 153 | default: 154 | return m_steam.get_axis(static_cast(id.code)) / 32767.0F; 155 | } 156 | case EV_KEY: 157 | return m_steam.get_button(static_cast(id.code)); 158 | } 159 | return 0; 160 | } 161 | 162 | int InputDeviceSteam::ff_upload(const ff_effect &eff) 163 | { 164 | return 0; 165 | } 166 | int InputDeviceSteam::ff_erase(int id) 167 | { 168 | return 0; 169 | } 170 | void InputDeviceSteam::ff_run(int eff, bool on) 171 | { 172 | if (on) 173 | { 174 | //printf("haptic on %d\n", eff); 175 | m_steam.haptic(true, 5000, 5000, 65535); 176 | m_steam.haptic(false, 5000, 5000, 65535); 177 | } 178 | else 179 | { 180 | //printf("haptic off %d\n", eff); 181 | m_steam.haptic(true, 1, 1, 1); 182 | m_steam.haptic(false, 1, 1, 1); 183 | } 184 | } 185 | 186 | void InputDeviceSteam::flush() 187 | { 188 | } 189 | 190 | -------------------------------------------------------------------------------- /inputsteam.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017, Rodrigo Rivas Costa 4 | 5 | This file is part of inputmap. 6 | 7 | inputmap is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | inputmap is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with inputmap. If not, see . 19 | 20 | */ 21 | 22 | #ifndef INPUTSTEAM_H_INCLUDED 23 | #define INPUTSTEAM_H_INCLUDED 24 | 25 | #include "inifile.h" 26 | #include "inputdev.h" 27 | #include "steam/steamcontroller.h" 28 | 29 | class InputDeviceSteam : public InputDevice 30 | { 31 | public: 32 | explicit InputDeviceSteam(const IniSection &ini); 33 | 34 | virtual int fd() 35 | { return m_steam.fd(); } 36 | virtual ValueId parse_value(const std::string &name); 37 | virtual PollResult on_poll(int event); 38 | virtual value_t get_value(const ValueId &id); 39 | virtual int ff_upload(const ff_effect &eff); 40 | virtual int ff_erase(int id); 41 | virtual void ff_run(int eff, bool on); 42 | virtual void flush(); 43 | private: 44 | SteamController m_steam; 45 | bool m_accel_enabled = false; 46 | bool m_auto_haptic_left, m_auto_haptic_right; 47 | }; 48 | 49 | #endif /* INPUTSTEAM_H_INCLUDED */ 50 | -------------------------------------------------------------------------------- /lemon/lempar.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2000-05-29 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** 7 | ** May you do good and not evil. 8 | ** May you find forgiveness for yourself and forgive others. 9 | ** May you share freely, never taking more than you give. 10 | ** 11 | ************************************************************************* 12 | ** Driver template for the LEMON parser generator. 13 | ** 14 | ** The "lemon" program processes an LALR(1) input grammar file, then uses 15 | ** this template to construct a parser. The "lemon" program inserts text 16 | ** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the 17 | ** interstitial "-" characters) contained in this template is changed into 18 | ** the value of the %name directive from the grammar. Otherwise, the content 19 | ** of this template is copied straight through into the generate parser 20 | ** source file. 21 | ** 22 | ** The following is the concatenation of all %include directives from the 23 | ** input grammar file: 24 | */ 25 | #include 26 | /************ Begin %include sections from the grammar ************************/ 27 | %% 28 | /**************** End of %include directives **********************************/ 29 | /* These constants specify the various numeric values for terminal symbols 30 | ** in a format understandable to "makeheaders". This section is blank unless 31 | ** "lemon" is run with the "-m" command-line option. 32 | ***************** Begin makeheaders token definitions *************************/ 33 | %% 34 | /**************** End makeheaders token definitions ***************************/ 35 | 36 | /* The next sections is a series of control #defines. 37 | ** various aspects of the generated parser. 38 | ** YYCODETYPE is the data type used to store the integer codes 39 | ** that represent terminal and non-terminal symbols. 40 | ** "unsigned char" is used if there are fewer than 41 | ** 256 symbols. Larger types otherwise. 42 | ** YYNOCODE is a number of type YYCODETYPE that is not used for 43 | ** any terminal or nonterminal symbol. 44 | ** YYFALLBACK If defined, this indicates that one or more tokens 45 | ** (also known as: "terminal symbols") have fall-back 46 | ** values which should be used if the original symbol 47 | ** would not parse. This permits keywords to sometimes 48 | ** be used as identifiers, for example. 49 | ** YYACTIONTYPE is the data type used for "action codes" - numbers 50 | ** that indicate what to do in response to the next 51 | ** token. 52 | ** ParseTOKENTYPE is the data type used for minor type for terminal 53 | ** symbols. Background: A "minor type" is a semantic 54 | ** value associated with a terminal or non-terminal 55 | ** symbols. For example, for an "ID" terminal symbol, 56 | ** the minor type might be the name of the identifier. 57 | ** Each non-terminal can have a different minor type. 58 | ** Terminal symbols all have the same minor type, though. 59 | ** This macros defines the minor type for terminal 60 | ** symbols. 61 | ** YYMINORTYPE is the data type used for all minor types. 62 | ** This is typically a union of many types, one of 63 | ** which is ParseTOKENTYPE. The entry in the union 64 | ** for terminal symbols is called "yy0". 65 | ** YYSTACKDEPTH is the maximum depth of the parser's stack. If 66 | ** zero the stack is dynamically sized using realloc() 67 | ** ParseARG_SDECL A static variable declaration for the %extra_argument 68 | ** ParseARG_PDECL A parameter declaration for the %extra_argument 69 | ** ParseARG_STORE Code to store %extra_argument into yypParser 70 | ** ParseARG_FETCH Code to extract %extra_argument from yypParser 71 | ** YYERRORSYMBOL is the code number of the error symbol. If not 72 | ** defined, then do no error processing. 73 | ** YYNSTATE the combined number of states. 74 | ** YYNRULE the number of rules in the grammar 75 | ** YY_MAX_SHIFT Maximum value for shift actions 76 | ** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions 77 | ** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions 78 | ** YY_MIN_REDUCE Maximum value for reduce actions 79 | ** YY_ERROR_ACTION The yy_action[] code for syntax error 80 | ** YY_ACCEPT_ACTION The yy_action[] code for accept 81 | ** YY_NO_ACTION The yy_action[] code for no-op 82 | */ 83 | #ifndef INTERFACE 84 | # define INTERFACE 1 85 | #endif 86 | /************* Begin control #defines *****************************************/ 87 | %% 88 | /************* End control #defines *******************************************/ 89 | 90 | /* Define the yytestcase() macro to be a no-op if is not already defined 91 | ** otherwise. 92 | ** 93 | ** Applications can choose to define yytestcase() in the %include section 94 | ** to a macro that can assist in verifying code coverage. For production 95 | ** code the yytestcase() macro should be turned off. But it is useful 96 | ** for testing. 97 | */ 98 | #ifndef yytestcase 99 | # define yytestcase(X) 100 | #endif 101 | 102 | 103 | /* Next are the tables used to determine what action to take based on the 104 | ** current state and lookahead token. These tables are used to implement 105 | ** functions that take a state number and lookahead value and return an 106 | ** action integer. 107 | ** 108 | ** Suppose the action integer is N. Then the action is determined as 109 | ** follows 110 | ** 111 | ** 0 <= N <= YY_MAX_SHIFT Shift N. That is, push the lookahead 112 | ** token onto the stack and goto state N. 113 | ** 114 | ** N between YY_MIN_SHIFTREDUCE Shift to an arbitrary state then 115 | ** and YY_MAX_SHIFTREDUCE reduce by rule N-YY_MIN_SHIFTREDUCE. 116 | ** 117 | ** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE 118 | ** and YY_MAX_REDUCE 119 | ** 120 | ** N == YY_ERROR_ACTION A syntax error has occurred. 121 | ** 122 | ** N == YY_ACCEPT_ACTION The parser accepts its input. 123 | ** 124 | ** N == YY_NO_ACTION No such action. Denotes unused 125 | ** slots in the yy_action[] table. 126 | ** 127 | ** The action table is constructed as a single large table named yy_action[]. 128 | ** Given state S and lookahead X, the action is computed as either: 129 | ** 130 | ** (A) N = yy_action[ yy_shift_ofst[S] + X ] 131 | ** (B) N = yy_default[S] 132 | ** 133 | ** The (A) formula is preferred. The B formula is used instead if: 134 | ** (1) The yy_shift_ofst[S]+X value is out of range, or 135 | ** (2) yy_lookahead[yy_shift_ofst[S]+X] is not equal to X, or 136 | ** (3) yy_shift_ofst[S] equal YY_SHIFT_USE_DFLT. 137 | ** (Implementation note: YY_SHIFT_USE_DFLT is chosen so that 138 | ** YY_SHIFT_USE_DFLT+X will be out of range for all possible lookaheads X. 139 | ** Hence only tests (1) and (2) need to be evaluated.) 140 | ** 141 | ** The formulas above are for computing the action when the lookahead is 142 | ** a terminal symbol. If the lookahead is a non-terminal (as occurs after 143 | ** a reduce action) then the yy_reduce_ofst[] array is used in place of 144 | ** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of 145 | ** YY_SHIFT_USE_DFLT. 146 | ** 147 | ** The following are the tables generated in this section: 148 | ** 149 | ** yy_action[] A single table containing all actions. 150 | ** yy_lookahead[] A table containing the lookahead for each entry in 151 | ** yy_action. Used to detect hash collisions. 152 | ** yy_shift_ofst[] For each state, the offset into yy_action for 153 | ** shifting terminals. 154 | ** yy_reduce_ofst[] For each state, the offset into yy_action for 155 | ** shifting non-terminals after a reduce. 156 | ** yy_default[] Default action for each state. 157 | ** 158 | *********** Begin parsing tables **********************************************/ 159 | %% 160 | /********** End of lemon-generated parsing tables *****************************/ 161 | 162 | /* The next table maps tokens (terminal symbols) into fallback tokens. 163 | ** If a construct like the following: 164 | ** 165 | ** %fallback ID X Y Z. 166 | ** 167 | ** appears in the grammar, then ID becomes a fallback token for X, Y, 168 | ** and Z. Whenever one of the tokens X, Y, or Z is input to the parser 169 | ** but it does not parse, the type of the token is changed to ID and 170 | ** the parse is retried before an error is thrown. 171 | ** 172 | ** This feature can be used, for example, to cause some keywords in a language 173 | ** to revert to identifiers if they keyword does not apply in the context where 174 | ** it appears. 175 | */ 176 | #ifdef YYFALLBACK 177 | static const YYCODETYPE yyFallback[] = { 178 | %% 179 | }; 180 | #endif /* YYFALLBACK */ 181 | 182 | /* The following structure represents a single element of the 183 | ** parser's stack. Information stored includes: 184 | ** 185 | ** + The state number for the parser at this level of the stack. 186 | ** 187 | ** + The value of the token stored at this level of the stack. 188 | ** (In other words, the "major" token.) 189 | ** 190 | ** + The semantic value stored at this level of the stack. This is 191 | ** the information used by the action routines in the grammar. 192 | ** It is sometimes called the "minor" token. 193 | ** 194 | ** After the "shift" half of a SHIFTREDUCE action, the stateno field 195 | ** actually contains the reduce action for the second half of the 196 | ** SHIFTREDUCE. 197 | */ 198 | struct yyStackEntry { 199 | YYACTIONTYPE stateno; /* The state-number, or reduce action in SHIFTREDUCE */ 200 | YYCODETYPE major; /* The major token value. This is the code 201 | ** number for the token at this stack level */ 202 | YYMINORTYPE minor; /* The user-supplied minor token value. This 203 | ** is the value of the token */ 204 | }; 205 | typedef struct yyStackEntry yyStackEntry; 206 | 207 | /* The state of the parser is completely contained in an instance of 208 | ** the following structure */ 209 | struct yyParser { 210 | yyStackEntry *yytos; /* Pointer to top element of the stack */ 211 | #ifdef YYTRACKMAXSTACKDEPTH 212 | int yyhwm; /* High-water mark of the stack */ 213 | #endif 214 | #ifndef YYNOERRORRECOVERY 215 | int yyerrcnt; /* Shifts left before out of the error */ 216 | #endif 217 | ParseARG_SDECL /* A place to hold %extra_argument */ 218 | #if YYSTACKDEPTH<=0 219 | int yystksz; /* Current side of the stack */ 220 | yyStackEntry *yystack; /* The parser's stack */ 221 | yyStackEntry yystk0; /* First stack entry */ 222 | #else 223 | yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ 224 | #endif 225 | }; 226 | typedef struct yyParser yyParser; 227 | 228 | #ifndef NDEBUG 229 | #include 230 | static FILE *yyTraceFILE = 0; 231 | static char *yyTracePrompt = 0; 232 | #endif /* NDEBUG */ 233 | 234 | #ifndef NDEBUG 235 | /* 236 | ** Turn parser tracing on by giving a stream to which to write the trace 237 | ** and a prompt to preface each trace message. Tracing is turned off 238 | ** by making either argument NULL 239 | ** 240 | ** Inputs: 241 | **
    242 | **
  • A FILE* to which trace output should be written. 243 | ** If NULL, then tracing is turned off. 244 | **
  • A prefix string written at the beginning of every 245 | ** line of trace output. If NULL, then tracing is 246 | ** turned off. 247 | **
248 | ** 249 | ** Outputs: 250 | ** None. 251 | */ 252 | void ParseTrace(FILE *TraceFILE, char *zTracePrompt){ 253 | yyTraceFILE = TraceFILE; 254 | yyTracePrompt = zTracePrompt; 255 | if( yyTraceFILE==0 ) yyTracePrompt = 0; 256 | else if( yyTracePrompt==0 ) yyTraceFILE = 0; 257 | } 258 | #endif /* NDEBUG */ 259 | 260 | #ifndef NDEBUG 261 | /* For tracing shifts, the names of all terminals and nonterminals 262 | ** are required. The following table supplies these names */ 263 | static const char *const yyTokenName[] = { 264 | %% 265 | }; 266 | #endif /* NDEBUG */ 267 | 268 | #ifndef NDEBUG 269 | /* For tracing reduce actions, the names of all rules are required. 270 | */ 271 | static const char *const yyRuleName[] = { 272 | %% 273 | }; 274 | #endif /* NDEBUG */ 275 | 276 | 277 | #if YYSTACKDEPTH<=0 278 | /* 279 | ** Try to increase the size of the parser stack. Return the number 280 | ** of errors. Return 0 on success. 281 | */ 282 | static int yyGrowStack(yyParser *p){ 283 | int newSize; 284 | int idx; 285 | yyStackEntry *pNew; 286 | 287 | newSize = p->yystksz*2 + 100; 288 | idx = p->yytos ? (int)(p->yytos - p->yystack) : 0; 289 | if( p->yystack==&p->yystk0 ){ 290 | pNew = malloc(newSize*sizeof(pNew[0])); 291 | if( pNew ) pNew[0] = p->yystk0; 292 | }else{ 293 | pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); 294 | } 295 | if( pNew ){ 296 | p->yystack = pNew; 297 | p->yytos = &p->yystack[idx]; 298 | #ifndef NDEBUG 299 | if( yyTraceFILE ){ 300 | fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n", 301 | yyTracePrompt, p->yystksz, newSize); 302 | } 303 | #endif 304 | p->yystksz = newSize; 305 | } 306 | return pNew==0; 307 | } 308 | #endif 309 | 310 | /* Datatype of the argument to the memory allocated passed as the 311 | ** second argument to ParseAlloc() below. This can be changed by 312 | ** putting an appropriate #define in the %include section of the input 313 | ** grammar. 314 | */ 315 | #ifndef YYMALLOCARGTYPE 316 | # define YYMALLOCARGTYPE size_t 317 | #endif 318 | 319 | /* 320 | ** This function allocates a new parser. 321 | ** The only argument is a pointer to a function which works like 322 | ** malloc. 323 | ** 324 | ** Inputs: 325 | ** A pointer to the function used to allocate memory. 326 | ** 327 | ** Outputs: 328 | ** A pointer to a parser. This pointer is used in subsequent calls 329 | ** to Parse and ParseFree. 330 | */ 331 | void *ParseAlloc(void *(*mallocProc)(YYMALLOCARGTYPE)){ 332 | yyParser *pParser; 333 | pParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) ); 334 | if( pParser ){ 335 | #ifdef YYTRACKMAXSTACKDEPTH 336 | pParser->yyhwm = 0; 337 | #endif 338 | #if YYSTACKDEPTH<=0 339 | pParser->yytos = NULL; 340 | pParser->yystack = NULL; 341 | pParser->yystksz = 0; 342 | if( yyGrowStack(pParser) ){ 343 | pParser->yystack = &pParser->yystk0; 344 | pParser->yystksz = 1; 345 | } 346 | #endif 347 | #ifndef YYNOERRORRECOVERY 348 | pParser->yyerrcnt = -1; 349 | #endif 350 | pParser->yytos = pParser->yystack; 351 | pParser->yystack[0].stateno = 0; 352 | pParser->yystack[0].major = 0; 353 | } 354 | return pParser; 355 | } 356 | 357 | /* The following function deletes the "minor type" or semantic value 358 | ** associated with a symbol. The symbol can be either a terminal 359 | ** or nonterminal. "yymajor" is the symbol code, and "yypminor" is 360 | ** a pointer to the value to be deleted. The code used to do the 361 | ** deletions is derived from the %destructor and/or %token_destructor 362 | ** directives of the input grammar. 363 | */ 364 | static void yy_destructor( 365 | yyParser *yypParser, /* The parser */ 366 | YYCODETYPE yymajor, /* Type code for object to destroy */ 367 | YYMINORTYPE *yypminor /* The object to be destroyed */ 368 | ){ 369 | ParseARG_FETCH; 370 | switch( yymajor ){ 371 | /* Here is inserted the actions which take place when a 372 | ** terminal or non-terminal is destroyed. This can happen 373 | ** when the symbol is popped from the stack during a 374 | ** reduce or during error processing or when a parser is 375 | ** being destroyed before it is finished parsing. 376 | ** 377 | ** Note: during a reduce, the only symbols destroyed are those 378 | ** which appear on the RHS of the rule, but which are *not* used 379 | ** inside the C code. 380 | */ 381 | /********* Begin destructor definitions ***************************************/ 382 | %% 383 | /********* End destructor definitions *****************************************/ 384 | default: break; /* If no destructor action specified: do nothing */ 385 | } 386 | } 387 | 388 | /* 389 | ** Pop the parser's stack once. 390 | ** 391 | ** If there is a destructor routine associated with the token which 392 | ** is popped from the stack, then call it. 393 | */ 394 | static void yy_pop_parser_stack(yyParser *pParser){ 395 | yyStackEntry *yytos; 396 | assert( pParser->yytos!=0 ); 397 | assert( pParser->yytos > pParser->yystack ); 398 | yytos = pParser->yytos--; 399 | #ifndef NDEBUG 400 | if( yyTraceFILE ){ 401 | fprintf(yyTraceFILE,"%sPopping %s\n", 402 | yyTracePrompt, 403 | yyTokenName[yytos->major]); 404 | } 405 | #endif 406 | yy_destructor(pParser, yytos->major, &yytos->minor); 407 | } 408 | 409 | /* 410 | ** Deallocate and destroy a parser. Destructors are called for 411 | ** all stack elements before shutting the parser down. 412 | ** 413 | ** If the YYPARSEFREENEVERNULL macro exists (for example because it 414 | ** is defined in a %include section of the input grammar) then it is 415 | ** assumed that the input pointer is never NULL. 416 | */ 417 | void ParseFree( 418 | void *p, /* The parser to be deleted */ 419 | void (*freeProc)(void*) /* Function used to reclaim memory */ 420 | ){ 421 | yyParser *pParser = (yyParser*)p; 422 | #ifndef YYPARSEFREENEVERNULL 423 | if( pParser==0 ) return; 424 | #endif 425 | while( pParser->yytos>pParser->yystack ) yy_pop_parser_stack(pParser); 426 | #if YYSTACKDEPTH<=0 427 | if( pParser->yystack!=&pParser->yystk0 ) free(pParser->yystack); 428 | #endif 429 | (*freeProc)((void*)pParser); 430 | } 431 | 432 | /* 433 | ** Return the peak depth of the stack for a parser. 434 | */ 435 | #ifdef YYTRACKMAXSTACKDEPTH 436 | int ParseStackPeak(void *p){ 437 | yyParser *pParser = (yyParser*)p; 438 | return pParser->yyhwm; 439 | } 440 | #endif 441 | 442 | /* 443 | ** Find the appropriate action for a parser given the terminal 444 | ** look-ahead token iLookAhead. 445 | */ 446 | static unsigned int yy_find_shift_action( 447 | yyParser *pParser, /* The parser */ 448 | YYCODETYPE iLookAhead /* The look-ahead token */ 449 | ){ 450 | int i; 451 | int stateno = pParser->yytos->stateno; 452 | 453 | if( stateno>=YY_MIN_REDUCE ) return stateno; 454 | assert( stateno <= YY_SHIFT_COUNT ); 455 | do{ 456 | i = yy_shift_ofst[stateno]; 457 | assert( iLookAhead!=YYNOCODE ); 458 | i += iLookAhead; 459 | if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ 460 | #ifdef YYFALLBACK 461 | YYCODETYPE iFallback; /* Fallback token */ 462 | if( iLookAhead %s\n", 467 | yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); 468 | } 469 | #endif 470 | assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */ 471 | iLookAhead = iFallback; 472 | continue; 473 | } 474 | #endif 475 | #ifdef YYWILDCARD 476 | { 477 | int j = i - iLookAhead + YYWILDCARD; 478 | if( 479 | #if YY_SHIFT_MIN+YYWILDCARD<0 480 | j>=0 && 481 | #endif 482 | #if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT 483 | j0 486 | ){ 487 | #ifndef NDEBUG 488 | if( yyTraceFILE ){ 489 | fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n", 490 | yyTracePrompt, yyTokenName[iLookAhead], 491 | yyTokenName[YYWILDCARD]); 492 | } 493 | #endif /* NDEBUG */ 494 | return yy_action[j]; 495 | } 496 | } 497 | #endif /* YYWILDCARD */ 498 | return yy_default[stateno]; 499 | }else{ 500 | return yy_action[i]; 501 | } 502 | }while(1); 503 | } 504 | 505 | /* 506 | ** Find the appropriate action for a parser given the non-terminal 507 | ** look-ahead token iLookAhead. 508 | */ 509 | static int yy_find_reduce_action( 510 | int stateno, /* Current state number */ 511 | YYCODETYPE iLookAhead /* The look-ahead token */ 512 | ){ 513 | int i; 514 | #ifdef YYERRORSYMBOL 515 | if( stateno>YY_REDUCE_COUNT ){ 516 | return yy_default[stateno]; 517 | } 518 | #else 519 | assert( stateno<=YY_REDUCE_COUNT ); 520 | #endif 521 | i = yy_reduce_ofst[stateno]; 522 | assert( i!=YY_REDUCE_USE_DFLT ); 523 | assert( iLookAhead!=YYNOCODE ); 524 | i += iLookAhead; 525 | #ifdef YYERRORSYMBOL 526 | if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ 527 | return yy_default[stateno]; 528 | } 529 | #else 530 | assert( i>=0 && iyytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); 547 | /* Here code is inserted which will execute if the parser 548 | ** stack every overflows */ 549 | /******** Begin %stack_overflow code ******************************************/ 550 | %% 551 | /******** End %stack_overflow code ********************************************/ 552 | ParseARG_STORE; /* Suppress warning about unused %extra_argument var */ 553 | } 554 | 555 | /* 556 | ** Print tracing information for a SHIFT action 557 | */ 558 | #ifndef NDEBUG 559 | static void yyTraceShift(yyParser *yypParser, int yyNewState){ 560 | if( yyTraceFILE ){ 561 | if( yyNewStateyytos->major], 564 | yyNewState); 565 | }else{ 566 | fprintf(yyTraceFILE,"%sShift '%s'\n", 567 | yyTracePrompt,yyTokenName[yypParser->yytos->major]); 568 | } 569 | } 570 | } 571 | #else 572 | # define yyTraceShift(X,Y) 573 | #endif 574 | 575 | /* 576 | ** Perform a shift action. 577 | */ 578 | static void yy_shift( 579 | yyParser *yypParser, /* The parser to be shifted */ 580 | int yyNewState, /* The new state to shift in */ 581 | int yyMajor, /* The major token to shift in */ 582 | ParseTOKENTYPE yyMinor /* The minor token to shift in */ 583 | ){ 584 | yyStackEntry *yytos; 585 | yypParser->yytos++; 586 | #ifdef YYTRACKMAXSTACKDEPTH 587 | if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){ 588 | yypParser->yyhwm++; 589 | assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) ); 590 | } 591 | #endif 592 | #if YYSTACKDEPTH>0 593 | if( yypParser->yytos>=&yypParser->yystack[YYSTACKDEPTH] ){ 594 | yypParser->yytos--; 595 | yyStackOverflow(yypParser); 596 | return; 597 | } 598 | #else 599 | if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz] ){ 600 | if( yyGrowStack(yypParser) ){ 601 | yypParser->yytos--; 602 | yyStackOverflow(yypParser); 603 | return; 604 | } 605 | } 606 | #endif 607 | if( yyNewState > YY_MAX_SHIFT ){ 608 | yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE; 609 | } 610 | yytos = yypParser->yytos; 611 | yytos->stateno = (YYACTIONTYPE)yyNewState; 612 | yytos->major = (YYCODETYPE)yyMajor; 613 | yytos->minor.yy0 = yyMinor; 614 | yyTraceShift(yypParser, yyNewState); 615 | } 616 | 617 | /* The following table contains information about every rule that 618 | ** is used during the reduce. 619 | */ 620 | static const struct { 621 | YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ 622 | unsigned char nrhs; /* Number of right-hand side symbols in the rule */ 623 | } yyRuleInfo[] = { 624 | %% 625 | }; 626 | 627 | static void yy_accept(yyParser*); /* Forward Declaration */ 628 | 629 | /* 630 | ** Perform a reduce action and the shift that must immediately 631 | ** follow the reduce. 632 | */ 633 | static void yy_reduce( 634 | yyParser *yypParser, /* The parser */ 635 | unsigned int yyruleno /* Number of the rule by which to reduce */ 636 | ){ 637 | int yygoto; /* The next state */ 638 | int yyact; /* The next action */ 639 | yyStackEntry *yymsp; /* The top of the parser's stack */ 640 | int yysize; /* Amount to pop the stack */ 641 | ParseARG_FETCH; 642 | yymsp = yypParser->yytos; 643 | #ifndef NDEBUG 644 | if( yyTraceFILE && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){ 645 | yysize = yyRuleInfo[yyruleno].nrhs; 646 | fprintf(yyTraceFILE, "%sReduce [%s], go to state %d.\n", yyTracePrompt, 647 | yyRuleName[yyruleno], yymsp[-yysize].stateno); 648 | } 649 | #endif /* NDEBUG */ 650 | 651 | /* Check that the stack is large enough to grow by a single entry 652 | ** if the RHS of the rule is empty. This ensures that there is room 653 | ** enough on the stack to push the LHS value */ 654 | if( yyRuleInfo[yyruleno].nrhs==0 ){ 655 | #ifdef YYTRACKMAXSTACKDEPTH 656 | if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){ 657 | yypParser->yyhwm++; 658 | assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack)); 659 | } 660 | #endif 661 | #if YYSTACKDEPTH>0 662 | if( yypParser->yytos>=&yypParser->yystack[YYSTACKDEPTH-1] ){ 663 | yyStackOverflow(yypParser); 664 | return; 665 | } 666 | #else 667 | if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){ 668 | if( yyGrowStack(yypParser) ){ 669 | yyStackOverflow(yypParser); 670 | return; 671 | } 672 | yymsp = yypParser->yytos; 673 | } 674 | #endif 675 | } 676 | 677 | switch( yyruleno ){ 678 | /* Beginning here are the reduction cases. A typical example 679 | ** follows: 680 | ** case 0: 681 | ** #line 682 | ** { ... } // User supplied code 683 | ** #line 684 | ** break; 685 | */ 686 | /********** Begin reduce actions **********************************************/ 687 | %% 688 | /********** End reduce actions ************************************************/ 689 | }; 690 | assert( yyrulenoYY_MAX_SHIFT ){ 696 | yyact += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE; 697 | } 698 | yymsp -= yysize-1; 699 | yypParser->yytos = yymsp; 700 | yymsp->stateno = (YYACTIONTYPE)yyact; 701 | yymsp->major = (YYCODETYPE)yygoto; 702 | yyTraceShift(yypParser, yyact); 703 | }else{ 704 | assert( yyact == YY_ACCEPT_ACTION ); 705 | yypParser->yytos -= yysize; 706 | yy_accept(yypParser); 707 | } 708 | } 709 | 710 | /* 711 | ** The following code executes when the parse fails 712 | */ 713 | #ifndef YYNOERRORRECOVERY 714 | static void yy_parse_failed( 715 | yyParser *yypParser /* The parser */ 716 | ){ 717 | ParseARG_FETCH; 718 | #ifndef NDEBUG 719 | if( yyTraceFILE ){ 720 | fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); 721 | } 722 | #endif 723 | while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); 724 | /* Here code is inserted which will be executed whenever the 725 | ** parser fails */ 726 | /************ Begin %parse_failure code ***************************************/ 727 | %% 728 | /************ End %parse_failure code *****************************************/ 729 | ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ 730 | } 731 | #endif /* YYNOERRORRECOVERY */ 732 | 733 | /* 734 | ** The following code executes when a syntax error first occurs. 735 | */ 736 | static void yy_syntax_error( 737 | yyParser *yypParser, /* The parser */ 738 | int yymajor, /* The major type of the error token */ 739 | ParseTOKENTYPE yyminor /* The minor type of the error token */ 740 | ){ 741 | ParseARG_FETCH; 742 | #define TOKEN yyminor 743 | /************ Begin %syntax_error code ****************************************/ 744 | %% 745 | /************ End %syntax_error code ******************************************/ 746 | ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ 747 | } 748 | 749 | /* 750 | ** The following is executed when the parser accepts 751 | */ 752 | static void yy_accept( 753 | yyParser *yypParser /* The parser */ 754 | ){ 755 | ParseARG_FETCH; 756 | #ifndef NDEBUG 757 | if( yyTraceFILE ){ 758 | fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); 759 | } 760 | #endif 761 | #ifndef YYNOERRORRECOVERY 762 | yypParser->yyerrcnt = -1; 763 | #endif 764 | assert( yypParser->yytos==yypParser->yystack ); 765 | /* Here code is inserted which will be executed whenever the 766 | ** parser accepts */ 767 | /*********** Begin %parse_accept code *****************************************/ 768 | %% 769 | /*********** End %parse_accept code *******************************************/ 770 | ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ 771 | } 772 | 773 | /* The main parser program. 774 | ** The first argument is a pointer to a structure obtained from 775 | ** "ParseAlloc" which describes the current state of the parser. 776 | ** The second argument is the major token number. The third is 777 | ** the minor token. The fourth optional argument is whatever the 778 | ** user wants (and specified in the grammar) and is available for 779 | ** use by the action routines. 780 | ** 781 | ** Inputs: 782 | **
    783 | **
  • A pointer to the parser (an opaque structure.) 784 | **
  • The major token number. 785 | **
  • The minor token number. 786 | **
  • An option argument of a grammar-specified type. 787 | **
788 | ** 789 | ** Outputs: 790 | ** None. 791 | */ 792 | void Parse( 793 | void *yyp, /* The parser */ 794 | int yymajor, /* The major token code number */ 795 | ParseTOKENTYPE yyminor /* The value for the token */ 796 | ParseARG_PDECL /* Optional %extra_argument parameter */ 797 | ){ 798 | YYMINORTYPE yyminorunion; 799 | unsigned int yyact; /* The parser action. */ 800 | #if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY) 801 | int yyendofinput; /* True if we are at the end of input */ 802 | #endif 803 | #ifdef YYERRORSYMBOL 804 | int yyerrorhit = 0; /* True if yymajor has invoked an error */ 805 | #endif 806 | yyParser *yypParser; /* The parser */ 807 | 808 | yypParser = (yyParser*)yyp; 809 | assert( yypParser->yytos!=0 ); 810 | #if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY) 811 | yyendofinput = (yymajor==0); 812 | #endif 813 | ParseARG_STORE; 814 | 815 | #ifndef NDEBUG 816 | if( yyTraceFILE ){ 817 | fprintf(yyTraceFILE,"%sInput '%s'\n",yyTracePrompt,yyTokenName[yymajor]); 818 | } 819 | #endif 820 | 821 | do{ 822 | yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor); 823 | if( yyact <= YY_MAX_SHIFTREDUCE ){ 824 | yy_shift(yypParser,yyact,yymajor,yyminor); 825 | #ifndef YYNOERRORRECOVERY 826 | yypParser->yyerrcnt--; 827 | #endif 828 | yymajor = YYNOCODE; 829 | }else if( yyact <= YY_MAX_REDUCE ){ 830 | yy_reduce(yypParser,yyact-YY_MIN_REDUCE); 831 | }else{ 832 | assert( yyact == YY_ERROR_ACTION ); 833 | yyminorunion.yy0 = yyminor; 834 | #ifdef YYERRORSYMBOL 835 | int yymx; 836 | #endif 837 | #ifndef NDEBUG 838 | if( yyTraceFILE ){ 839 | fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); 840 | } 841 | #endif 842 | #ifdef YYERRORSYMBOL 843 | /* A syntax error has occurred. 844 | ** The response to an error depends upon whether or not the 845 | ** grammar defines an error token "ERROR". 846 | ** 847 | ** This is what we do if the grammar does define ERROR: 848 | ** 849 | ** * Call the %syntax_error function. 850 | ** 851 | ** * Begin popping the stack until we enter a state where 852 | ** it is legal to shift the error symbol, then shift 853 | ** the error symbol. 854 | ** 855 | ** * Set the error count to three. 856 | ** 857 | ** * Begin accepting and shifting new tokens. No new error 858 | ** processing will occur until three tokens have been 859 | ** shifted successfully. 860 | ** 861 | */ 862 | if( yypParser->yyerrcnt<0 ){ 863 | yy_syntax_error(yypParser,yymajor,yyminor); 864 | } 865 | yymx = yypParser->yytos->major; 866 | if( yymx==YYERRORSYMBOL || yyerrorhit ){ 867 | #ifndef NDEBUG 868 | if( yyTraceFILE ){ 869 | fprintf(yyTraceFILE,"%sDiscard input token %s\n", 870 | yyTracePrompt,yyTokenName[yymajor]); 871 | } 872 | #endif 873 | yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion); 874 | yymajor = YYNOCODE; 875 | }else{ 876 | while( yypParser->yytos >= yypParser->yystack 877 | && yymx != YYERRORSYMBOL 878 | && (yyact = yy_find_reduce_action( 879 | yypParser->yytos->stateno, 880 | YYERRORSYMBOL)) >= YY_MIN_REDUCE 881 | ){ 882 | yy_pop_parser_stack(yypParser); 883 | } 884 | if( yypParser->yytos < yypParser->yystack || yymajor==0 ){ 885 | yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); 886 | yy_parse_failed(yypParser); 887 | #ifndef YYNOERRORRECOVERY 888 | yypParser->yyerrcnt = -1; 889 | #endif 890 | yymajor = YYNOCODE; 891 | }else if( yymx!=YYERRORSYMBOL ){ 892 | yy_shift(yypParser,yyact,YYERRORSYMBOL,yyminor); 893 | } 894 | } 895 | yypParser->yyerrcnt = 3; 896 | yyerrorhit = 1; 897 | #elif defined(YYNOERRORRECOVERY) 898 | /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to 899 | ** do any kind of error recovery. Instead, simply invoke the syntax 900 | ** error routine and continue going as if nothing had happened. 901 | ** 902 | ** Applications can set this macro (for example inside %include) if 903 | ** they intend to abandon the parse upon the first syntax error seen. 904 | */ 905 | yy_syntax_error(yypParser,yymajor, yyminor); 906 | yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); 907 | yymajor = YYNOCODE; 908 | 909 | #else /* YYERRORSYMBOL is not defined */ 910 | /* This is what we do if the grammar does not define ERROR: 911 | ** 912 | ** * Report an error message, and throw away the input token. 913 | ** 914 | ** * If the input token is $, then fail the parse. 915 | ** 916 | ** As before, subsequent error messages are suppressed until 917 | ** three input tokens have been successfully shifted. 918 | */ 919 | if( yypParser->yyerrcnt<=0 ){ 920 | yy_syntax_error(yypParser,yymajor, yyminor); 921 | } 922 | yypParser->yyerrcnt = 3; 923 | yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); 924 | if( yyendofinput ){ 925 | yy_parse_failed(yypParser); 926 | #ifndef YYNOERRORRECOVERY 927 | yypParser->yyerrcnt = -1; 928 | #endif 929 | } 930 | yymajor = YYNOCODE; 931 | #endif 932 | } 933 | }while( yymajor!=YYNOCODE && yypParser->yytos>yypParser->yystack ); 934 | #ifndef NDEBUG 935 | if( yyTraceFILE ){ 936 | yyStackEntry *i; 937 | char cDiv = '['; 938 | fprintf(yyTraceFILE,"%sReturn. Stack=",yyTracePrompt); 939 | for(i=&yypParser->yystack[1]; i<=yypParser->yytos; i++){ 940 | fprintf(yyTraceFILE,"%c%s", cDiv, yyTokenName[i->major]); 941 | cDiv = ' '; 942 | } 943 | fprintf(yyTraceFILE,"]\n"); 944 | } 945 | #endif 946 | return; 947 | } 948 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('inputmap', ['c', 'cpp'], default_options : ['cpp_std=gnu++11']) 2 | 3 | 4 | lemon_exe = executable('lemon', ['lemon/lemon.c'], native : true) 5 | 6 | lemon = generator(lemon_exe, 7 | output : ['@BASENAME@.cpp', '@BASENAME@.h'], 8 | arguments : ['@INPUT@', '-T@SOURCE_DIR@/lemon/lempar.c', '-B@BUILD_DIR@']) 9 | 10 | udevdep = meson.get_compiler('cpp').find_library('udev') 11 | includes = include_directories('util') 12 | 13 | devinput_src = lemon.process('devinput.lem') 14 | 15 | executable('inputmap', 16 | ['inputmap.cpp', 'inifile.cpp', 'inputdev.cpp', 'outputdev.cpp', 'event-codes.cpp', 'steam/steamcontroller.cpp', 'inputsteam.cpp', 17 | 'devinput-parser.cpp', devinput_src], 18 | include_directories: includes, 19 | dependencies: [udevdep], 20 | install: true, 21 | ) 22 | 23 | 24 | -------------------------------------------------------------------------------- /outputdev.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017, Rodrigo Rivas Costa 4 | 5 | This file is part of inputmap. 6 | 7 | inputmap is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | inputmap is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with inputmap. If not, see . 19 | 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include "outputdev.h" 26 | #include "inputdev.h" 27 | #include "event-codes.h" 28 | #include "devinput-parser.h" 29 | 30 | OutputDevice::OutputDevice(const IniSection &ini, IInputByName &inputFinder) 31 | { 32 | std::string name = ini.find_single_value("name"); 33 | std::string phys = ini.find_single_value("phys"); 34 | std::string bus = ini.find_single_value("bus"); 35 | std::string vendor = ini.find_single_value("vendor"); 36 | std::string product = ini.find_single_value("product"); 37 | std::string version = ini.find_single_value("version"); 38 | 39 | if (name.empty()) 40 | name = "InputMap"; 41 | uinput_setup us = {}; 42 | if (!bus.empty()) 43 | us.id.bustype = bus_id(bus.c_str()); 44 | else 45 | us.id.bustype = BUS_VIRTUAL; 46 | us.id.version = parse_int(version, 1); 47 | us.id.vendor = parse_hex_int(vendor, 0); 48 | us.id.product = parse_hex_int(product, 0); 49 | 50 | strcpy(us.name, name.c_str()); 51 | 52 | m_fd = FD_open("/dev/uinput", O_RDWR); 53 | test(ioctl(m_fd.get(), UI_SET_PHYS, phys.c_str()), "UI_SET_PHYS"); 54 | 55 | bool has_rel = false; 56 | for (const auto &kv : g_rel_names) 57 | { 58 | if (!kv.name) 59 | continue; 60 | std::string ref = ini.find_single_value(kv.name); 61 | if (ref.empty()) 62 | continue; 63 | m_rel.emplace_back(kv.id, parse_ref(ref, inputFinder)); 64 | if (!has_rel) 65 | { 66 | test(ioctl(m_fd.get(), UI_SET_EVBIT, EV_REL), "EV_REL"); 67 | has_rel = true; 68 | } 69 | test(ioctl(m_fd.get(), UI_SET_RELBIT, kv.id), "UI_SET_RELBIT"); 70 | } 71 | 72 | bool has_key = false; 73 | for (const auto &kv : g_key_names) 74 | { 75 | if (!kv.name) 76 | continue; 77 | std::string ref = ini.find_single_value(kv.name); 78 | if (ref.empty()) 79 | continue; 80 | m_key.emplace_back(kv.id, parse_ref(ref, inputFinder)); 81 | if (!has_key) 82 | { 83 | test(ioctl(m_fd.get(), UI_SET_EVBIT, EV_KEY), "EV_KEY"); 84 | has_key = true; 85 | } 86 | test(ioctl(m_fd.get(), UI_SET_KEYBIT, kv.id), "UI_SET_KEYBIT"); 87 | } 88 | 89 | bool has_abs = false; 90 | for (const auto &kv : g_abs_names) 91 | { 92 | if (!kv.name) 93 | continue; 94 | std::string ref = ini.find_single_value(kv.name); 95 | if (ref.empty()) 96 | continue; 97 | m_abs.emplace_back(kv.id, parse_ref(ref, inputFinder)); 98 | if (!has_abs) 99 | { 100 | test(ioctl(m_fd.get(), UI_SET_EVBIT, EV_ABS), "EV_ABS"); 101 | has_abs = true; 102 | } 103 | uinput_abs_setup abs = {}; 104 | abs.code = kv.id; 105 | abs.absinfo.minimum = -32767; 106 | abs.absinfo.maximum = 32767; //TODO: configure ABS range 107 | test(ioctl(m_fd.get(), UI_ABS_SETUP, &abs), "abs"); 108 | } 109 | 110 | bool has_ff = false; 111 | for (const auto &kv : g_ff_names) 112 | { 113 | if (!kv.name) 114 | continue; 115 | std::string ref = ini.find_single_value(kv.name); 116 | if (ref.empty()) 117 | continue; 118 | auto pref = parse_ref(ref, inputFinder); 119 | auto xref = dynamic_cast(pref.get()); 120 | if (!xref || xref->get_value_id().type != EV_FF || xref->get_value_id().code != kv.id) 121 | { 122 | throw std::runtime_error("FF ref must be a simple reference to the same FF value"); 123 | } 124 | pref.release(); 125 | m_ff.emplace_back(kv.id, std::unique_ptr(xref)); 126 | if (!has_ff) 127 | { 128 | us.ff_effects_max = 16; 129 | test(ioctl(m_fd.get(), UI_SET_EVBIT, EV_FF), "EV_FF"); 130 | has_ff = true; 131 | } 132 | test(ioctl(m_fd.get(), UI_SET_FFBIT, kv.id), "UI_SET_FFBIT"); 133 | } 134 | 135 | test(ioctl(m_fd.get(), UI_DEV_SETUP, &us), "UI_DEV_SETUP"); 136 | test(ioctl(m_fd.get(), UI_DEV_CREATE, 0), "UI_DEV_CREATE"); 137 | } 138 | 139 | inline input_event create_event(int type, int code, int value) 140 | { 141 | input_event ev; 142 | ev.type = type; 143 | ev.code = code; 144 | ev.value = value; 145 | return ev; 146 | } 147 | 148 | inline void do_event(std::vector &evs, int type, int code, ValueExpr *ref) 149 | { 150 | if (!ref) 151 | return; 152 | 153 | value_t value = ref->get_value(); 154 | if (type == EV_ABS) 155 | value *= 32767; 156 | evs.push_back(create_event(type, code, static_cast(value))); 157 | } 158 | 159 | void OutputDevice::sync() 160 | { 161 | std::vector evs; 162 | 163 | for (auto &v: m_rel) 164 | do_event(evs, EV_REL, v.first, v.second.get()); 165 | for (auto &v: m_key) 166 | do_event(evs, EV_KEY, v.first, v.second.get()); 167 | for (auto &v: m_abs) 168 | do_event(evs, EV_ABS, v.first, v.second.get()); 169 | 170 | if (!evs.empty()) 171 | { 172 | evs.push_back(create_event(EV_SYN, SYN_REPORT, 0)); 173 | test(write(m_fd.get(), evs.data(), evs.size() * sizeof(input_event)), "write"); 174 | } 175 | } 176 | 177 | ValueRef *OutputDevice::get_ff(int id) 178 | { 179 | for (auto &v: m_ff) 180 | if (v.first == id) 181 | return v.second.get(); 182 | return nullptr; 183 | } 184 | 185 | PollResult OutputDevice::on_poll(int event) 186 | { 187 | if ((event & EPOLLIN) == 0) 188 | return PollResult::None; 189 | input_event ev; 190 | 191 | int res = read(fd(), &ev, sizeof(input_event)); 192 | if (res == -1) 193 | { 194 | if (errno == EINTR) 195 | return PollResult::None; 196 | perror("output read"); 197 | return PollResult::Error; 198 | } 199 | 200 | //printf("EV %d %d %d\n", ev.type, ev.code, ev.value); 201 | 202 | switch (ev.type) 203 | { 204 | case EV_UINPUT: 205 | switch (ev.code) 206 | { 207 | case UI_FF_UPLOAD: 208 | { 209 | uinput_ff_upload ff{}; 210 | ff.request_id = ev.value; 211 | test(ioctl(m_fd.get(), UI_BEGIN_FF_UPLOAD, &ff), "UI_BEGIN_FF_UPLOAD"); 212 | //printf("UPLOAD 0x%X, id=%d (%d, %d)\n", ff.effect.type, ff.effect.id, ff.effect.u.rumble.weak_magnitude, ff.effect.u.rumble.strong_magnitude); 213 | ValueRef *ffout = get_ff(ff.effect.type); 214 | auto device = ffout ? ffout->get_device() : nullptr; 215 | int out_id = ff.effect.id; 216 | int in_id = out_id < 0 || !device ? -EINVAL : device->ff_upload(ff.effect); 217 | ff.retval = in_id < 0 ? in_id : 0; 218 | test(ioctl(m_fd.get(), UI_END_FF_UPLOAD, &ff), "UI_END_FF_UPLOAD"); 219 | if (in_id >= 0) 220 | { 221 | if (static_cast(out_id) >= m_effects.size()) 222 | m_effects.resize(out_id + 1); 223 | auto &effect = m_effects[out_id]; 224 | effect.device = device; 225 | effect.input_id = in_id; 226 | } 227 | } 228 | break; 229 | case UI_FF_ERASE: 230 | { 231 | uinput_ff_erase ff{}; 232 | ff.request_id = ev.value; 233 | test(ioctl(m_fd.get(), UI_BEGIN_FF_ERASE, &ff), "UI_BEGIN_FF_ERASE"); 234 | //printf("ERASE %d\n", ff.effect_id); 235 | if (ff.effect_id < 0 || ff.effect_id >= m_effects.size()) 236 | { 237 | ff.retval = -EINVAL; 238 | } 239 | else 240 | { 241 | auto &effect = m_effects[ff.effect_id]; 242 | auto device = effect.device.lock(); 243 | int in_id = effect.input_id; 244 | effect = FFEffect{}; 245 | if (device) 246 | { 247 | int err = device->ff_erase(in_id); 248 | if (err < 0) 249 | ff.retval = err; 250 | else 251 | ff.retval = 0; 252 | } 253 | else 254 | { 255 | ff.retval = -EINVAL; 256 | } 257 | } 258 | test(ioctl(m_fd.get(), UI_END_FF_ERASE, &ff), "UI_END_FF_ERASE"); 259 | } 260 | break; 261 | } 262 | break; 263 | case EV_FF: 264 | { 265 | //printf("FF %s id=%d\n", ev.value? "start" : "stop", ev.code); 266 | if (ev.code >= 0 && ev.code < m_effects.size()) 267 | { 268 | auto &effect = m_effects[ev.code]; 269 | auto device = effect.device.lock(); 270 | if (device) 271 | device->ff_run(effect.input_id, ev.value != 0); 272 | } 273 | } 274 | break; 275 | } 276 | 277 | return PollResult::None; 278 | } 279 | 280 | -------------------------------------------------------------------------------- /outputdev.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017, Rodrigo Rivas Costa 4 | 5 | This file is part of inputmap. 6 | 7 | inputmap is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | inputmap is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with inputmap. If not, see . 19 | 20 | */ 21 | 22 | #ifndef OUTPUTDEV_H_INCLUDED 23 | #define OUTPUTDEV_H_INCLUDED 24 | 25 | #include "steam/fd.h" 26 | #include "inifile.h" 27 | #include "inputdev.h" 28 | #include "devinput-parser.h" 29 | 30 | struct FFEffect 31 | { 32 | std::weak_ptr device; 33 | int input_id; 34 | }; 35 | 36 | class OutputDevice : public IPollable 37 | { 38 | public: 39 | OutputDevice(const IniSection &ini, IInputByName &inputFinder); 40 | void sync(); 41 | 42 | virtual int fd() override { return m_fd.get(); } 43 | virtual PollResult on_poll(int event) override; 44 | 45 | private: 46 | FD m_fd; 47 | std::vector>> m_rel; 48 | std::vector>> m_key; 49 | std::vector>> m_abs; 50 | std::vector>> m_ff; 51 | 52 | ValueRef *get_ff(int id); 53 | void write_value(int type, int code, int value); 54 | 55 | std::vector m_effects; 56 | }; 57 | 58 | #endif /* OUTPUTDEV_H_INCLUDED */ 59 | -------------------------------------------------------------------------------- /quaternion.h: -------------------------------------------------------------------------------- 1 | #ifndef QUATERNION_H_INCLUDED 2 | #define QUATERNION_H_INCLUDED 3 | 4 | template 5 | class Quaternion 6 | { 7 | private: 8 | F m_data[4]; //w, x,y,z 9 | public: 10 | 11 | F w() const { return m_data[0]; } 12 | F x() const { return m_data[1]; } 13 | F y() const { return m_data[2]; } 14 | F z() const { return m_data[3]; } 15 | F &w() { return m_data[0]; } 16 | F &x() { return m_data[1]; } 17 | F &y() { return m_data[2]; } 18 | F &z() { return m_data[3]; } 19 | 20 | Quaternion() 21 | { 22 | w() = 1; 23 | x() = 0; 24 | y() = 0; 25 | z() = 0; 26 | } 27 | Quaternion(F fw, F fx, F fy, F fz) 28 | { 29 | w() = fw; 30 | x() = fx; 31 | y() = fy; 32 | z() = fz; 33 | } 34 | //The inverse of a unary quaternion is the conjugate, such as x*Conjugate(x) Equals Identity 35 | friend Quaternion Conjugate(const Quaternion &a) 36 | { 37 | return Quaternion(a.w(), -a.x(), -a.y(), -a.z()); 38 | } 39 | 40 | //The opposed of a unary quaternion is such as x*Opposed(x) equals a full turn 41 | friend Quaternion Opposed(const Quaternion &a) 42 | { 43 | return Quaternion(-a.w(), a.x(), a.y(), a.z()); 44 | } 45 | 46 | friend Quaternion operator*(const Quaternion &p, const Quaternion &q) 47 | { 48 | return Quaternion( 49 | p.w()*q.w() - p.x()*q.x() - p.y()*q.y() - p.z()*q.z(), 50 | p.x()*q.w() + p.w()*q.x() + p.y()*q.z() - p.z()*q.y(), 51 | p.y()*q.w() + p.w()*q.y() + p.z()*q.x() - p.x()*q.z(), 52 | p.z()*q.w() + p.w()*q.z() + p.x()*q.y() - p.y()*q.x() 53 | ); 54 | } 55 | 56 | void Transform(F &x, F &y, F &z) const 57 | { 58 | F aa = this->w()*this->w(), bb = this->x()*this->x(), 59 | cc = this->y()*this->y(), dd = this->z()*this->z(); 60 | 61 | F rx = 2*(this->w()*(this->y()*z - this->z()*y) + this->x()*(this->y()*y + this->z()*z)) + 62 | x*(aa + bb - cc - dd); 63 | F ry = 2*(this->z()*(this->y()*z + this->w()*x) + this->x()*(this->y()*x - this->w()*z)) + 64 | y*(aa - bb + cc - dd); 65 | F rz = 2*(this->x()*(this->w()*y + this->z()*x) + this->y()*(this->z()*y - this->w()*x)) + 66 | z*(aa - bb - cc + dd); 67 | 68 | x = rx; 69 | y = ry; 70 | z = rz; 71 | } 72 | void ToAngles(F &roll, F &pitch, F &yaw) const 73 | { 74 | F ysqr = y() * y(); 75 | 76 | // roll (x-axis rotation) 77 | F t0 = +2.0 * (w() * x() + y() * z()); 78 | F t1 = +1.0 - 2.0 * (x() * x() + ysqr); 79 | roll = atan2(t0, t1); 80 | 81 | // pitch (y-axis rotation) 82 | F t2 = +2.0 * (w() * y() - z() * x()); 83 | t2 = t2 > 1 ? 1 : t2; 84 | t2 = t2 < -1 ? -1 : t2; 85 | pitch = asin(t2); 86 | 87 | // yaw (z-axis rotation) 88 | F t3 = 2 * (w() * z() + x() * y()); 89 | F t4 = 1 - 2 * (ysqr + z() * z()); 90 | yaw = atan2(t3, t4); 91 | } 92 | F AngleHalf() const 93 | { 94 | if (w() > F(1.0)) 95 | return 0; 96 | else if (w() < F(-1.0)) 97 | return F(M_PI); 98 | else 99 | return acos(w()); 100 | } 101 | void ToAxis(F &x, F &y, F &z, F &angle) const 102 | { 103 | if (fabs(1-w()) < F(0.0001F) || 104 | fabs(1+w()) < F(0.0001F)) 105 | { 106 | angle = 0; 107 | x = 1, y = 0, z = 0; 108 | } 109 | F angle_2 = AngleHalf(); 110 | angle = 2 * angle_2; 111 | 112 | F si = sin(angle_2); // sqrt(1 - w()*w()); 113 | x = this->x() / si; 114 | y = this->y() / si; 115 | z = this->z() / si; 116 | } 117 | 118 | }; 119 | 120 | 121 | #endif /* QUATERNION_H_INCLUDED */ 122 | -------------------------------------------------------------------------------- /steam/LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /steam/fd.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017, Rodrigo Rivas Costa 4 | 5 | This file is part of inputmap. 6 | 7 | inputmap is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | inputmap is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with inputmap. If not, see . 19 | 20 | */ 21 | 22 | #ifndef FD_H_INCLUDED 23 | #define FD_H_INCLUDED 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "unique_handle.h" 30 | 31 | struct FileCloser 32 | { 33 | typedef UniqueHandle pointer; 34 | void operator()(pointer p) noexcept 35 | { 36 | close(p); 37 | } 38 | }; 39 | typedef std::unique_ptr FD; 40 | 41 | inline void test(int res, const char *txt) 42 | { 43 | if (res < 0) 44 | { 45 | int e = errno; 46 | std::string msg = txt; 47 | msg += ": "; 48 | msg += strerror(e); 49 | throw std::runtime_error(msg); 50 | } 51 | } 52 | 53 | inline FD FD_open(const char *name, int flags) 54 | { 55 | int fd = open(name, flags); 56 | test(fd, name); 57 | return FD(fd); 58 | } 59 | 60 | inline bool test_bit(int bit, const unsigned char *ptr) 61 | { 62 | return (ptr[bit >> 3] >> (bit & 7)) & 1; 63 | } 64 | 65 | template 66 | constexpr size_t countof(T (&a)[N]) 67 | { 68 | return N; 69 | } 70 | 71 | #endif /* FD_H_INCLUDED */ 72 | -------------------------------------------------------------------------------- /steam/steamcontroller.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017, Rodrigo Rivas Costa 4 | 5 | This file is part of inputmap. 6 | 7 | inputmap is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | inputmap is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with inputmap. If not, see . 19 | 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "steamcontroller.h" 28 | #include "fd.h" 29 | #include "udev-wrapper.h" 30 | 31 | /************************ 32 | * SteamController interface: 33 | 34 | * CONNECT: 35 | idx: 0 8 16 24 32 40 48 56 64 36 | ex: 01 00 03 01 02 05 00 00-00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00- 37 | v-/--/ | | | 38 | type(3)-/ | | 39 | size ------/ | 40 | conn ---------/ 41 | 42 | conn: 43 | * 1 Disconnected 44 | * 2 Connected 45 | 46 | * STATUS: 47 | idx: 0 8 16 24 32 40 48 56 64 48 | ex: 01 00 04 0B BB FD 02 00-00 00 00 00 32 14 64 00-00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00- 49 | v-/--/ | | | | | | | | | 50 | type(4)-/ | | | | | | | | 51 | size ------/ | | | | | | | 52 | seq ---------/--/--/--/ | | | 53 | volt ---------------------------------/--/ | 54 | batt%---------------------------------------/ 55 | 56 | * INPUT: 57 | idx: 0 8 16 24 32 40 48 56 64 58 | ex: 01 00 01 3C B9 0F 0F 00-00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00-00 00 00 00 55 00 20 FD-4D 40 FF FF 01 00 02 00-EE 7C 46 FD 46 00 4B E4-00 00 78 01 7A 01 FE 01-F7 01 00 00 00 00 31 14- 59 | v-/--/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 60 | type(1)-/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 61 | size ------/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 62 | seq ---------/--/--/--/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 63 | btn ---------------------/--/--/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 64 | tl ------------------------------/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 65 | tr ---------------------------------/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 66 | lx ---------------------------------------------/--/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 67 | ly ---------------------------------------------------/--/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 68 | rx ---------------------------------------------------------/--/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 69 | ry ---------------------------------------------------------------/--/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 70 | *tl16 ---------------------------------------------------------------------/--/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 71 | *tr16 ---------------------------------------------------------------------------/--/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 72 | *acc1 ---------------------------------------------------------------------------------/--/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 73 | *acc2 ---------------------------------------------------------------------------------------/--/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 74 | *acc3 ---------------------------------------------------------------------------------------------/--/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | 75 | gyr1 ---------------------------------------------------------------------------------------------------/--/ | | | | | | | | | | | | | | | | | | | | | | | | | | 76 | gyr2 ---------------------------------------------------------------------------------------------------------/--/ | | | | | | | | | | | | | | | | | | | | | | | | 77 | gyr3 ---------------------------------------------------------------------------------------------------------------/--/ | | | | | | | | | | | | | | | | | | | | | | 78 | quat1---------------------------------------------------------------------------------------------------------------------/--/ | | | | | | | | | | | | | | | | | | | | 79 | quat2---------------------------------------------------------------------------------------------------------------------------/--/ | | | | | | | | | | | | | | | | | | 80 | quat3---------------------------------------------------------------------------------------------------------------------------------/--/ | | | | | | | | | | | | | | | | 81 | quat4---------------------------------------------------------------------------------------------------------------------------------------/--/ | | | | | | | | | | | | | | 82 | *tlraw---------------------------------------------------------------------------------------------------------------------------------------------------/--/ | | | | | | | | | | | | 83 | *trraw---------------------------------------------------------------------------------------------------------------------------------------------------------/--/ | | | | | | | | | | 84 | *jxraw---------------------------------------------------------------------------------------------------------------------------------------------------------------/--/ | | | | | | | | 85 | *jyraw---------------------------------------------------------------------------------------------------------------------------------------------------------------------/--/ | | | | | | 86 | *ux ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------/--/ | | | | 87 | *uy ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------/--/ | | 88 | *volt ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------/--/ 89 | 90 | *: not sent over wireless 91 | 92 | Known commands: 93 | 94 | * 0x81: Init? 95 | * 0x83: return version info. The reply looks like a list of 5 byte struct { uint8_t id; uint32_t data }. The IDs are: 96 | - 0x00: firmware version? 97 | - 0x01: product id (0x1102=Steam Controller) 98 | - 0x02: capabilities? (0x03) 99 | - 0x04: firmware build time 100 | - 0x05: some unknown time 101 | - 0x0A: bootloader firmware build time 102 | * 0xAE: return serial info: (15 00: board?; 15 01: serial) 103 | * 0xBA: return unknown info 104 | * 0x87: write register (LEN REG BL BH). LEN in bytes (= 3 * #writes) 105 | * 0xC5: ??? (03 FF FF FF) 106 | * 0xC1: ??? (10 FFFFFFFF 030905FF FFFFFFFF FFFFFFFF) 107 | * 0x8F: haptic feedback (LEN SIDE ONL ONH OFFL OFFH CNTL CNTH UNK) 108 | (LEN=7 or 8) Ex: rmouse=8f 08 00 6400 0000 0100 00 109 | SIDE: 0=right; 1=left. 110 | ON: time the motor is on each pulse, in us. 111 | OFF: time the motor is off each pulse, in us. 112 | CNT: number of pulses. 113 | * 0x85: enable key emulation 114 | * 0x8E: enable mouse & cursor emulation 115 | 116 | Known registers: 117 | 118 | * 0x30: Accelerometer. 0x00=disable, 0x14=enable. 119 | * 0x08: 0x07=disable mouse emulation 120 | * 0x07: 0x07=disable cursor emulation 121 | * 0x18: 0x00=remove margin in rpad 122 | * 0x2D: 0x64=??? 123 | * 0x2E: 0x00=??? 124 | * 0x31: 0x02=??? 125 | * 0x32: 0x012C=??? 126 | * 0x34-0x3B: 0x0A=??? 127 | *************/ 128 | 129 | SteamController::SteamController(FD fd) 130 | :m_fd(std::move(fd)) 131 | { 132 | memset(m_data, 0, sizeof(m_data)); 133 | 134 | //remove margin in rpad, we do this unconditionally 135 | write_register(0x18, 0); 136 | 137 | #if 0 138 | uint8_t reply[64] = {}; 139 | send_cmd({0x83}); 140 | recv_cmd(reply); 141 | for (int i = 0; i < 64; ++i) 142 | printf(" %02X", reply[i]); 143 | printf("\n"); 144 | #endif 145 | } 146 | 147 | SteamController::~SteamController() noexcept 148 | { 149 | try 150 | { 151 | if (m_fd) 152 | set_emulation_mode(static_cast(SteamEmulation::Keys | SteamEmulation::Cursor | SteamEmulation::Mouse)); 153 | } 154 | catch (...) 155 | { //ignore errors 156 | } 157 | } 158 | 159 | static inline uint16_t U2(const uint8_t *m, size_t x) 160 | { 161 | return m[x] | (m[x+1] << 8); 162 | } 163 | static inline int16_t S2(const uint8_t *m, size_t x) 164 | { 165 | int16_t res = U2(m, x); 166 | if (res == -32768) //or else you will get weird values when negating this value 167 | res = -32767; 168 | return res; 169 | } 170 | /*static inline uint32_t U4(const uint8_t *m, size_t x) 171 | { 172 | uint32_t b1 = U2(m, x); 173 | uint32_t b2 = U2(m, x+2); 174 | return b1 | (b2 << 16); 175 | } 176 | static inline int32_t S4(const uint8_t *m, size_t x) 177 | { 178 | return U4(m, x); 179 | }*/ 180 | static inline uint32_t U3(const uint8_t *m, size_t x) 181 | { 182 | uint32_t b1 = U2(m, x); 183 | uint32_t b2 = m[x+2]; 184 | return b1 | (b2 << 16); 185 | } 186 | 187 | static inline uint8_t BL(uint16_t x) 188 | { 189 | return x & 0xFF; 190 | } 191 | static inline uint8_t BH(uint16_t x) 192 | { 193 | return x >> 8; 194 | } 195 | 196 | bool SteamController::on_poll(int event) 197 | { 198 | if ((event & EPOLLIN) == 0) 199 | return false; 200 | 201 | uint8_t data[64]; 202 | int res = read(m_fd.get(), data, sizeof(data)); 203 | if (res == -1) 204 | { 205 | if (errno == EINTR) 206 | return false;; 207 | throw std::runtime_error("read error"); 208 | } 209 | if (res != sizeof(data)) 210 | return false; 211 | 212 | uint8_t type = data[2]; 213 | if (type == 3 && data[4] == 0x01) //Disconnect event 214 | throw std::runtime_error("steam controller powerer off"); 215 | 216 | if (type != 1) //input 217 | return false; 218 | 219 | memcpy(m_data, data, sizeof(m_data)); 220 | return true; 221 | } 222 | 223 | /* 224 | static int int_sign(int x) 225 | { 226 | if (x < 0) 227 | return -1; 228 | if (x > 0) 229 | return 1; 230 | return 0; 231 | } 232 | */ 233 | 234 | int SteamController::get_axis(SteamAxis axis) 235 | { 236 | uint32_t btn = U3(m_data, 8); 237 | switch (axis) 238 | { 239 | case SteamAxis::X: 240 | return S2(m_data, 16); 241 | case SteamAxis::Y: 242 | return -S2(m_data, 18); 243 | case SteamAxis::StickX: 244 | switch (btn & (SteamButton::LPadTouch | SteamButton::LPadAndJoy)) 245 | { 246 | case SteamButton::LPadTouch: 247 | return 0; 248 | case 0: 249 | case SteamButton::LPadAndJoy: 250 | return m_stickX = S2(m_data, 16); 251 | case SteamButton::LPadTouch | SteamButton::LPadAndJoy: 252 | return m_stickX; 253 | } 254 | case SteamAxis::StickY: 255 | switch (btn & (SteamButton::LPadTouch | SteamButton::LPadAndJoy)) 256 | { 257 | case SteamButton::LPadTouch: 258 | return 0; 259 | case 0: 260 | case SteamButton::LPadAndJoy: 261 | return m_stickY = -S2(m_data, 18); 262 | case SteamButton::LPadTouch | SteamButton::LPadAndJoy: 263 | return m_stickY; 264 | } 265 | case SteamAxis::LPadX: 266 | switch (btn & (SteamButton::LPadTouch | SteamButton::LPadAndJoy)) 267 | { 268 | case 0: 269 | return 0; 270 | case SteamButton::LPadTouch: 271 | case SteamButton::LPadTouch | SteamButton::LPadAndJoy: 272 | return m_lpadX = S2(m_data, 16); 273 | case SteamButton::LPadAndJoy: 274 | return m_lpadX; 275 | } 276 | case SteamAxis::LPadY: 277 | switch (btn & (SteamButton::LPadTouch | SteamButton::LPadAndJoy)) 278 | { 279 | case 0: 280 | return 0; 281 | case SteamButton::LPadTouch: 282 | case SteamButton::LPadTouch | SteamButton::LPadAndJoy: 283 | return m_lpadY = -S2(m_data, 18); 284 | case SteamButton::LPadAndJoy: 285 | return m_lpadY; 286 | } 287 | case SteamAxis::RPadX: 288 | return S2(m_data, 20); 289 | case SteamAxis::RPadY: 290 | return -S2(m_data, 22); 291 | case SteamAxis::LTrigger: 292 | return m_data[11]; 293 | case SteamAxis::RTrigger: 294 | return m_data[12]; 295 | case SteamAxis::GyroX: 296 | return S2(m_data, 34); 297 | case SteamAxis::GyroY: 298 | return S2(m_data, 36); 299 | case SteamAxis::GyroZ: 300 | return S2(m_data, 38); 301 | case SteamAxis::QuatW: 302 | return S2(m_data, 40); 303 | case SteamAxis::QuatX: 304 | return S2(m_data, 42); 305 | case SteamAxis::QuatY: 306 | return S2(m_data, 44); 307 | case SteamAxis::QuatZ: 308 | return S2(m_data, 46); 309 | default: 310 | return 0; 311 | } 312 | } 313 | 314 | bool SteamController::get_button(SteamButton btn) 315 | { 316 | if (btn == SteamButton::LPadTouch) 317 | btn = static_cast(SteamButton::LPadTouch | SteamButton::LPadAndJoy); 318 | uint32_t btns = U3(m_data, 8); 319 | return (btns & btn) != 0; 320 | } 321 | 322 | void SteamController::send_cmd(const std::initializer_list &data) 323 | { 324 | unsigned char feat[65] = {0}; 325 | memcpy(feat + 1, data.begin(), data.size()); 326 | 327 | timespec x; 328 | x.tv_sec = 0; 329 | x.tv_nsec = 50000000; // 50 ms 330 | 331 | //This command sometimes fails with EPIPE, particularly with the wireless device 332 | //so retry a few times before giving up. 333 | for (int i = 0; i < 10; ++i) // up to 500 ms 334 | { 335 | if (ioctl(m_fd.get(), HIDIOCSFEATURE(sizeof(feat)), feat) >= 0) 336 | return; 337 | nanosleep(&x, &x); 338 | } 339 | throw std::runtime_error("HIDIOCSFEATURE"); 340 | } 341 | 342 | void SteamController::recv_cmd(uint8_t reply[64]) 343 | { 344 | unsigned char feat[65] = {0}; 345 | test(ioctl(m_fd.get(), HIDIOCGFEATURE(sizeof(feat)), feat), "HIDIOCGFEATURE"); 346 | memcpy(reply, feat + 1, 64); 347 | } 348 | 349 | 350 | void SteamController::write_register(uint8_t reg, uint16_t value) 351 | { 352 | send_cmd({0x87, 0x03, reg, BL(value), BH(value)}); 353 | } 354 | 355 | 356 | void SteamController::set_accelerometer(bool enable) 357 | { 358 | write_register(0x30, enable? 0x14 : 0x00); 359 | } 360 | 361 | void SteamController::set_emulation_mode(SteamEmulation mode) 362 | { 363 | if (mode & SteamEmulation::Keys) 364 | send_cmd({0x85}); 365 | else 366 | send_cmd({0x81}); 367 | 368 | //I don't know how to enable these two separately, but I know how to disable them, so... 369 | switch (mode & (SteamEmulation::Cursor | SteamEmulation::Mouse)) 370 | { 371 | case 0: 372 | write_register(0x08, 0x07); 373 | write_register(0x07, 0x07); 374 | break; 375 | case SteamEmulation::Cursor: 376 | send_cmd({0x8e}); 377 | write_register(0x08, 0x07); 378 | break; 379 | case SteamEmulation::Mouse: 380 | send_cmd({0x8e}); 381 | write_register(0x07, 0x07); 382 | break; 383 | case SteamEmulation::Cursor | SteamEmulation::Mouse: 384 | send_cmd({0x8e}); 385 | break; 386 | } 387 | } 388 | 389 | void SteamController::haptic(bool left, int time_on, int time_off, int cycles) 390 | { 391 | send_cmd({0x8f, 0x08, 392 | BL(left? 1 : 0), 393 | BL(time_on), BH(time_on), 394 | BL(time_off), BH(time_off), 395 | BL(cycles), BH(cycles), 396 | 0, 397 | }); 398 | } 399 | 400 | void SteamController::haptic_freq(bool left, int freq, int duty, int duration) 401 | { 402 | int period = 1000000 / freq; //in us 403 | int time_on = period * duty / 100; 404 | int time_off = period - time_on; 405 | if (time_on <= 0 || time_off < 0) 406 | return; 407 | int cycles = duration / period; 408 | if (cycles <= 0) 409 | cycles = 1; 410 | haptic(left, time_on, time_off, cycles); 411 | } 412 | 413 | std::string SteamController::get_serial() 414 | { 415 | uint8_t reply[64] = {}; 416 | send_cmd({0xAE, 0x15, 0x01}); 417 | recv_cmd(reply); 418 | reply[13] = 0; 419 | std::string serial = reinterpret_cast(reply + 3); 420 | return serial; 421 | } 422 | 423 | std::string SteamController::get_board() 424 | { 425 | uint8_t reply[64] = {}; 426 | send_cmd({0xAE, 0x15, 0x00}); 427 | recv_cmd(reply); 428 | reply[13] = 0; 429 | std::string board = reinterpret_cast(reply + 3); 430 | return board; 431 | } 432 | 433 | static std::vector find_steam_devpaths() 434 | { 435 | std::vector res; 436 | 437 | udev_ptr ud { udev_new() }; 438 | 439 | std::vector sys_usbs = find_udev_devices(ud.get(), nullptr, "usb", "idVendor", "28de", "idProduct", "1102"); //wired 440 | std::vector sys_usbs2 = find_udev_devices(ud.get(), nullptr, "usb", "idVendor", "28de", "idProduct", "1142"); //wireless 441 | sys_usbs.insert(sys_usbs.end(), 442 | std::make_move_iterator(sys_usbs2.begin()), 443 | std::make_move_iterator(sys_usbs2.end())); 444 | for (const std::string &sys_usb : sys_usbs) 445 | { 446 | udev_device_ptr usb { udev_device_new_from_syspath(ud.get(), sys_usb.c_str()) }; 447 | 448 | for (const std::string &sys_itf : find_udev_devices(ud.get(), usb.get(), "usb", "bInterfaceProtocol", "00")) 449 | { 450 | udev_device_ptr itf { udev_device_new_from_syspath(ud.get(), sys_itf.c_str()) }; 451 | 452 | for (const std::string &sys_hid : find_udev_devices(ud.get(), itf.get(), "hidraw", nullptr, nullptr)) 453 | { 454 | udev_device_ptr hid { udev_device_new_from_syspath(ud.get(), sys_hid.c_str()) }; 455 | res.push_back(udev_device_get_devnode(hid.get())); 456 | } 457 | } 458 | } 459 | return res; 460 | } 461 | 462 | SteamController SteamController::Create(const std::string &serial) 463 | { 464 | std::vector devpaths = find_steam_devpaths(); 465 | for (const std::string &devpath : devpaths) 466 | { 467 | printf("Device %s\n", devpath.c_str()); 468 | FD fd { FD_open(devpath.c_str(), O_RDWR) }; 469 | SteamController sc(std::move(fd)); 470 | std::string detected = sc.get_serial(); 471 | printf("Serial '%s'\n", detected.c_str()); 472 | if (detected.empty()) 473 | continue; //no actual device 474 | //printf("Board '%s'\n", sc.get_board().c_str()); 475 | if (serial.empty() || serial == detected) 476 | return sc; 477 | } 478 | throw std::runtime_error("steam device not found"); 479 | } 480 | 481 | -------------------------------------------------------------------------------- /steam/steamcontroller.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017, Rodrigo Rivas Costa 4 | 5 | This file is part of inputmap. 6 | 7 | inputmap is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | inputmap is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with inputmap. If not, see . 19 | 20 | */ 21 | 22 | #ifndef STEAMCONTROLLER_H_INCLUDED 23 | #define STEAMCONTROLLER_H_INCLUDED 24 | 25 | #include "fd.h" 26 | 27 | enum SteamAxis 28 | { 29 | X, 30 | Y, 31 | StickX, 32 | StickY, 33 | LPadX, 34 | LPadY, 35 | RPadX, 36 | RPadY, 37 | LTrigger, 38 | RTrigger, 39 | GyroX, 40 | GyroY, 41 | GyroZ, 42 | QuatW, 43 | QuatX, 44 | QuatY, 45 | QuatZ, 46 | }; 47 | 48 | enum SteamButton 49 | { 50 | RTriggerFull = 0x000001, 51 | LTriggerFull = 0x000002, 52 | RBumper = 0x000004, 53 | LBumper = 0x000008, 54 | BtnY = 0x000010, 55 | BtnB = 0x000020, 56 | BtnX = 0x000040, 57 | BtnA = 0x000080, 58 | North = 0x000100, 59 | East = 0x000200, 60 | West = 0x000400, 61 | South = 0x000800, 62 | Menu = 0x001000, 63 | Logo = 0x002000, 64 | Escape = 0x004000, 65 | LBack = 0x008000, 66 | RBack = 0x010000, 67 | LPadClick = 0x020000, 68 | RPadClick = 0x040000, 69 | LPadTouch = 0x080000, 70 | RPadTouch = 0x100000, 71 | Unknown2 = 0x200000, 72 | Stick = 0x400000, 73 | LPadAndJoy = 0x800000, 74 | }; 75 | 76 | enum SteamEmulation 77 | { 78 | None = 0, 79 | Keys = 1, //enter, space, escape... 80 | Cursor = 2, //up, down, left, right 81 | Mouse = 4, //mouse cursor, left/right buttons 82 | }; 83 | 84 | class SteamController 85 | { 86 | public: 87 | static SteamController Create(const std::string &serial); 88 | ~SteamController() noexcept; 89 | SteamController(SteamController &&o) = default; 90 | 91 | int fd() 92 | { return m_fd.get(); } 93 | bool on_poll(int event); 94 | 95 | int get_axis(SteamAxis axis); 96 | bool get_button(SteamButton btn); 97 | 98 | void set_accelerometer(bool enable); 99 | void set_emulation_mode(SteamEmulation mode); 100 | 101 | //left: true=left pad, false=right pad 102 | //time_on, time_off: in us 103 | void haptic(bool left, int time_on, int time_off, int cycles); 104 | //freq: in Hz 105 | //duty: 0-100 % 106 | //duration: in us 107 | void haptic_freq(bool left, int freq, int duty, int duration); 108 | std::string get_serial(); 109 | std::string get_board(); 110 | 111 | private: 112 | FD m_fd; 113 | int16_t m_lpadX, m_lpadY, m_stickX, m_stickY; 114 | uint8_t m_data[64]; 115 | 116 | SteamController(FD fd); 117 | void send_cmd(const std::initializer_list &data); 118 | void recv_cmd(uint8_t reply[64]); 119 | void write_register(uint8_t reg, uint16_t value); 120 | }; 121 | 122 | #endif /* STEAMCONTROLLER_H_INCLUDED */ 123 | -------------------------------------------------------------------------------- /steam/udev-wrapper.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017, Rodrigo Rivas Costa 4 | 5 | This file is part of inputmap. 6 | 7 | inputmap is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | inputmap is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with inputmap. If not, see . 19 | 20 | */ 21 | 22 | #ifndef UDEV_WRAPPER_H_INCLUDED 23 | #define UDEV_WRAPPER_H_INCLUDED 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | struct udev_closer 31 | { 32 | void operator()(udev *p) noexcept { udev_unref(p); } 33 | }; 34 | using udev_ptr = std::unique_ptr; 35 | struct udev_device_closer 36 | { 37 | void operator()(udev_device *p) noexcept { udev_device_unref(p); } 38 | }; 39 | using udev_device_ptr = std::unique_ptr; 40 | struct udev_enumerate_closer 41 | { 42 | void operator()(udev_enumerate *p) noexcept { udev_enumerate_unref(p); } 43 | }; 44 | using udev_enumerate_ptr = std::unique_ptr; 45 | 46 | static inline std::vector find_udev_devices(udev *ud, udev_device *parent, const char *subsystem, 47 | const char *attr, const char *value, 48 | const char *attr2 = nullptr, const char *value2 = nullptr 49 | ) 50 | { 51 | std::vector res; 52 | udev_enumerate_ptr ude { udev_enumerate_new(ud) }; 53 | if (subsystem) 54 | udev_enumerate_add_match_subsystem(ude.get(), subsystem); 55 | if (attr) 56 | udev_enumerate_add_match_sysattr(ude.get(), attr, value); 57 | if (attr2) 58 | udev_enumerate_add_match_sysattr(ude.get(), attr2, value2); 59 | if (parent) 60 | udev_enumerate_add_match_parent(ude.get(), parent); 61 | udev_enumerate_scan_devices(ude.get()); 62 | for (udev_list_entry *le = udev_enumerate_get_list_entry(ude.get()); le; le = udev_list_entry_get_next(le)) 63 | { 64 | const char *name = udev_list_entry_get_name(le); 65 | res.push_back(name); 66 | } 67 | return res; 68 | } 69 | 70 | 71 | #endif /* UDEV-WRAPPER_H_INCLUDED */ 72 | -------------------------------------------------------------------------------- /util/unique_handle.h: -------------------------------------------------------------------------------- 1 | #ifndef UNIQUE_HANDLE_H_INCLUDED 2 | #define UNIQUE_HANDLE_H_INCLUDED 3 | 4 | #include 5 | 6 | template 7 | class UniqueHandle 8 | { 9 | public: 10 | UniqueHandle(std::nullptr_t = nullptr) noexcept 11 | :m_id(TNul) 12 | { } 13 | UniqueHandle(T x) noexcept 14 | :m_id(x) 15 | { } 16 | //T &operator*() { return m_id; } 17 | explicit operator bool() const noexcept { return m_id != TNul; } 18 | 19 | operator T&() noexcept { return m_id; } 20 | operator T() const noexcept { return m_id; } 21 | 22 | T *operator&() noexcept { return &m_id; } 23 | const T *operator&() const noexcept { return &m_id; } 24 | 25 | friend bool operator == (UniqueHandle a, UniqueHandle b) noexcept { return a.m_id == b.m_id; } 26 | friend bool operator != (UniqueHandle a, UniqueHandle b) noexcept { return a.m_id != b.m_id; } 27 | friend bool operator == (UniqueHandle a, std::nullptr_t) noexcept { return a.m_id == TNul; } 28 | friend bool operator != (UniqueHandle a, std::nullptr_t) noexcept { return a.m_id != TNul; } 29 | friend bool operator == (std::nullptr_t, UniqueHandle b) noexcept { return TNul == b.m_id; } 30 | friend bool operator != (std::nullptr_t, UniqueHandle b) noexcept { return TNul != b.m_id; } 31 | 32 | private: 33 | T m_id; 34 | }; 35 | 36 | /* Example of use: 37 | 38 | struct FooDeleter 39 | { 40 | typedef UniqueHandle pointer; 41 | void operator()(pointer p) noexcept 42 | { 43 | foo_free(p); 44 | } 45 | }; 46 | typedef std::unique_ptr Buffer; 47 | */ 48 | 49 | #endif /* UNIQUE_HANDLE_H_INCLUDED */ 50 | --------------------------------------------------------------------------------