Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/unbit/foohid
OSX IOKit driver for implementing virtual HID devices (joypads, keyboards, mices, ...) from userspace
https://github.com/unbit/foohid
Last synced: 4 months ago
JSON representation
OSX IOKit driver for implementing virtual HID devices (joypads, keyboards, mices, ...) from userspace
- Host: GitHub
- URL: https://github.com/unbit/foohid
- Owner: unbit
- License: mit
- Created: 2015-03-04T18:05:09.000Z (almost 10 years ago)
- Default Branch: master
- Last Pushed: 2018-02-10T10:18:14.000Z (almost 7 years ago)
- Last Synced: 2024-08-02T01:25:33.436Z (6 months ago)
- Language: C++
- Homepage:
- Size: 57.6 KB
- Stars: 264
- Watchers: 19
- Forks: 35
- Open Issues: 14
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# foohid
OSX IOKit driver for implementing virtual HID devices from userspace.
*** The foohid driver is currently unsupported and lacks proper thread-safety (leading to security problems), please do not use it
in production unless you want to sponsor the project contacting info at unbit dot it ***## Examples
You can check the [examples section below](#an-example-with-report-descriptor), the [examples directory](examples) or use the Python 2 wrapper (next section).### The Python 2 wrapper
You can use the official Python 2 wrapper (available on PyPi and [here](https://github.com/unbit/foohid-py)) to start playing with virtual HID devices.
First of all, install the `foohid` Python 2 extension:```sh
pip install foohid
```Now clone the `foohid-py` repository:
```sh
git clone https://github.com/unbit/foohid-py
cd foohid-py
```Three tests are available inside the repository directory:
* `test_mouse.py` will create a virtual mouse. Just run it and every second your mouse pointer will move to a random position of the screen.
* `test_joypad.py` will create a virtual joypad. Just run it and every second left and right axes will be updated.
* `test_list.py` will show the listing feature (a bunch of virtual devices will be created, then listed and destroyed).Note: Python 3 support should be ready soon.
### A C example (with report descriptor)
Building report descriptors is a really annoying task.
To simplify testing, the following describes a generic mouse (thanks [eleccelerator](http://eleccelerator.com/tutorial-about-usb-hid-report-descriptors/)).```c
unsigned char report_descriptor[] = {
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x01, // COLLECTION (Application)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x03, // USAGE_MAXIMUM (Button 3)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x03, // REPORT_COUNT (3)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x05, // REPORT_SIZE (5)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
}
```It maps to the following structure:
```c
struct mouse_report_t {
uint8_t buttons;
int8_t x;
int8_t y;
}
```To create and move around a virtual mouse:
```c
#include
#include
#include
#includeunsigned char report_descriptor[] = {
// ...
};struct mouse_report_t {
// ...
};#define SERVICE_NAME "it_unbit_foohid"
#define FOOHID_CREATE 0 // create selector
#define FOOHID_SEND 2 // send selector#define DEVICE_NAME "Foohid Virtual Mouse"
#define DEVICE_SN "SN 123456"int main() {
io_iterator_t iterator;
io_service_t service;
io_connect_t connect;// Get a reference to the IOService
kern_return_t ret = IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching(SERVICE_NAME), &iterator);if (ret != KERN_SUCCESS) {
printf("Unable to access IOService.\n");
exit(1);
}// Iterate till success
int found = 0;
while ((service = IOIteratorNext(iterator)) != IO_OBJECT_NULL) {
ret = IOServiceOpen(service, mach_task_self(), 0, &connect);if (ret == KERN_SUCCESS) {
found = 1;
break;
}IOObjectRelease(service);
}
IOObjectRelease(iterator);if (!found) {
printf("Unable to open IOService.\n");
exit(1);
}// Fill up the input arguments.
uint32_t input_count = 8;
uint64_t input[input_count];
input[0] = (uint64_t) strdup(DEVICE_NAME); // device name
input[1] = strlen((char *)input[0]); // name length
input[2] = (uint64_t) report_descriptor; // report descriptor
input[3] = sizeof(report_descriptor); // report descriptor len
input[4] = (uint64_t) strdup(DEVICE_SN); // serial number
input[5] = strlen((char *)input[4]); // serial number len
input[6] = (uint64_t) 2; // vendor ID
input[7] = (uint64_t) 3; // device IDret = IOConnectCallScalarMethod(connect, FOOHID_CREATE, input, input_count, NULL, 0);
if (ret != KERN_SUCCESS) {
printf("Unable to create HID device. May be fine if created previously.\n");
}// Arguments to be passed through the HID message.
struct mouse_report_t mouse;
uint32_t send_count = 4;
uint64_t send[send_count];
send[0] = (uint64_t)input[0]; // device name
send[1] = strlen((char *)input[0]); // name length
send[2] = (uint64_t) &mouse; // mouse struct
send[3] = sizeof(struct mouse_report_t); // mouse struct lenfor(;;) {
mouse.buttons = 0;
mouse.x = rand();
mouse.y = rand();ret = IOConnectCallScalarMethod(connect, FOOHID_SEND, send, send_count, NULL, 0);
if (ret != KERN_SUCCESS) {
printf("Unable to send message to HID device.\n");
}sleep(1); // sleep for a second
}
}
```#### Run it
You can found the previous example [here](examples/mouse.c).
Compile it and run it with:```bash
curl -O https://raw.githubusercontent.com/unbit/foohid/develop/examples/mouse.c
gcc mouse.c -o virtual_mouse -framework IOKit
./virtual_mouse
```### Other examples
A few other examples may be found within the [examples directory](examples).
One that is worth noticing is the [keyboard example](examples/keyboard.c).## The `IOUserClient` API specification
4 methods are exposed:
* `CREATE` (selector 0)
* `DESTROY` (selector 1)
* `SEND` (selector 2)
* `LIST` (selector 3)### Create
Creates a new fake/virtual HID device with the specified `report_descriptor`.Takes 8 input arguments:
1. name pointer.
2. name length.
3. `report_descriptor` pointer.
4. `report_descriptor` length.
5. device serial number pointer.
6. device serial number pointer length.
7. device vendor ID.
8. device product ID.And doesn't output any argument.
### Destroy
Takes 2 input arguments:
1. name pointer.
2. name length.And doesn't output any argument.
### Send
Generate a HID event from a previously created fake/virtual HID device.
Takes 4 input arguments:
1. name pointer.
2. name length.
3. `report_descriptor` pointer.
4. `report_descriptor` length.And doesn't output any argument.
### List
Return (into the supplied buffer pointer) the list of available fake/virtual devices separated by `\0`.
The items output value contains the number of returned items.
If the supplied buffer is not big enough, the needed bytes value contains a suggestion for a second run.Takes 2 input arguments:
1. buffer pointer.
2. buffer length.And 2 output arguments:
1. needed bytes (suggestion for next run)
2. returned items.## Logging
Logging is disabled by default.
You can enable it by building a DEBUG version by using XCode, or manually setting the `-DDEBUG` preprocessor flag.