// gcc -Wall xinput2_ivy.c -lX11 -lXi -livy #include #include #include #include #include #include /* for isdigit */ #include #include #include #include #include #define REGEXP "^ub" static char *ivy_bus = NULL; static int xi_opcode; static char *application_name="xinput2_ivy"; static char *identifier="default"; static char *device_name=NULL; static XIDeviceInfo *device_info=NULL; static char **valuators_names=NULL; static double *valuators_min=NULL; static double *valuators_max=NULL; static int num_valuators=0; /*void Callback (IvyClientPtr app, void *user_data, int argc, char *argv[]) { MsgRcvPtr *ptr = (MsgRcvPtr *) user_data; printf ("%s sent unbind message, unbinding to %s\n", IvyGetApplicationName(app),REGEXP); IvyUnbindMsg(*ptr); } */ static const char* type_to_name(int evtype) { const char *name; switch(evtype) { case XI_DeviceChanged: name = "DeviceChanged"; break; case XI_KeyPress: name = "KeyPress"; break; case XI_KeyRelease: name = "KeyRelease"; break; case XI_ButtonPress: name = "ButtonPress"; break; case XI_ButtonRelease: name = "ButtonRelease"; break; case XI_Motion: name = "Motion"; break; case XI_Enter: name = "Enter"; break; case XI_Leave: name = "Leave"; break; case XI_FocusIn: name = "FocusIn"; break; case XI_FocusOut: name = "FocusOut"; break; case XI_HierarchyChanged: name = "HierarchyChanged"; break; case XI_PropertyEvent: name = "PropertyEvent"; break; case XI_RawKeyPress: name = "RawKeyPress"; break; case XI_RawKeyRelease: name = "RawKeyRelease"; break; case XI_RawButtonPress: name = "RawButtonPress"; break; case XI_RawButtonRelease: name = "RawButtonRelease"; break; case XI_RawMotion: name = "RawMotion"; break; case XI_RawTouchBegin: name = "RawTouchBegin"; break; case XI_RawTouchUpdate: name = "RawTouchUpdate"; break; case XI_RawTouchEnd: name = "RawTouchEnd"; break; default: name = "unknown event type"; break; } return name; } static void init_valuators_definitions (Display *display, XIDeviceInfo* device) { int i; valuators_names = calloc(device->num_classes, sizeof(char *)); valuators_min = calloc(device->num_classes, sizeof(double)); valuators_max = calloc(device->num_classes, sizeof(double)); num_valuators = 0; for (i = 0; i < device->num_classes; i++) { switch(device->classes[i]->type) { case XIButtonClass: { XIButtonClassInfo *b = (XIButtonClassInfo*)(device->classes[i]); char *name; int j; for (j = 0; j < b->num_buttons; j++) { name = (b->labels[j]) ? XGetAtomName(display, b->labels[j]) : NULL; printf("%s, ", (name) ? name : "None"); XFree(name); } printf("\n"); break; } case XIKeyClass: break; case XIValuatorClass: { XIValuatorClassInfo *v = (XIValuatorClassInfo*)(device->classes[i]); char *name = v->label ? XGetAtomName(display, v->label) : "unknown"; int size = strlen(name); int j; for (j=0; j < size; j++) { if (name[j] == ' ') { name[j] = '_'; } } valuators_names[v->number] = memcpy(malloc(size + 1), name, size + 1); if (v->label) { XFree(name); } printf("%d %f->%f\n", v->number, v->min, v->max); valuators_min[v->number] = v->min; valuators_max[v->number] = v->max; num_valuators++; break; } } } } static void register_hierarchy_changed (Display *display) { XIEventMask mask; mask.deviceid = XIAllDevices; mask.mask_len = XIMaskLen(XI_LASTEVENT); mask.mask = calloc(mask.mask_len, sizeof(char)); XISetMask(mask.mask, XI_HierarchyChanged); XISelectEvents(display, DefaultRootWindow(display), &mask, 1); free(mask.mask); } static void register_events (Display *display, XIDeviceInfo* device) { XIEventMask mask; mask.deviceid = device->deviceid; mask.mask_len = XIMaskLen(XI_LASTEVENT); mask.mask = calloc(mask.mask_len, sizeof(char)); XISetMask(mask.mask, XI_RawKeyPress); XISetMask(mask.mask, XI_RawKeyRelease); XISetMask(mask.mask, XI_RawButtonPress); XISetMask(mask.mask, XI_RawButtonRelease); XISetMask(mask.mask, XI_RawMotion); XISetMask(mask.mask, XI_RawTouchBegin); XISetMask(mask.mask, XI_RawTouchUpdate); XISetMask(mask.mask, XI_RawTouchEnd); XISetMask(mask.mask, XI_TouchBegin); XISetMask(mask.mask, XI_TouchUpdate); XISetMask(mask.mask, XI_TouchEnd); init_valuators_definitions(display, device); XISelectEvents(display, DefaultRootWindow(display), &mask, 1); free(mask.mask); } static void unregister_events (Display *display, int deviceid) { printf("Unregistering device %d\n", deviceid); XIEventMask mask; mask.deviceid = deviceid; mask.mask_len = XIMaskLen(XI_LASTEVENT); mask.mask = calloc(mask.mask_len, sizeof(char)); /* alternative to clear mask is to use 0 as length */ XISelectEvents(display, DefaultRootWindow(display), &mask, 1); free(mask.mask); } static XIDeviceInfo* find_device_info (Display *display, char *name) { XIDeviceInfo *info; int ndevices; Bool is_id = True; int i, id = -1; for(i = 0; i < strlen(name); i++) { if (!isdigit(name[i])) { is_id = False; break; } } if (is_id) { id = atoi(name); } info = XIQueryDevice(display, XIAllDevices, &ndevices); if (is_id) { for(i = 0; i < ndevices; i++) { if (info[i].deviceid == id) { return &info[i]; } } } else { for(i = 0; i < ndevices; i++) { if (strcmp(info[i].name, name) == 0) { return &info[i]; } } } XIFreeDeviceInfo(info); return NULL; } static void hierarchy_changed_callback (Display *display, XIHierarchyEvent *event) { if (event->flags & (XIDeviceEnabled | XIDeviceDisabled)) { if (device_info) { int i; for (i = 0; i < event->num_info; i++) { if (event->info[i].deviceid == device_info->deviceid) { if (!event->info[i].enabled) { printf("*** Device %s now disabled\n", device_name); int i; for (i=0; i < num_valuators; i++) { free(valuators_names[i]); } free(valuators_names); free(valuators_min); free(valuators_max); device_info = NULL; break; } } } } else { // device_info = find_device_info (event->display, device_name); device_info = find_device_info (display, device_name); if (device_info) { printf("*** Device %s now enabled (name=%s, id=%d)\n", device_name, device_info->name, device_info->deviceid); // register_events(event->display, device_info); register_events(display, device_info); } } } } static void raw_button_callback (Display *display, XIRawEvent *event, int type) { int ndevices; XIDeviceInfo* device = XIQueryDevice(display, event->deviceid, &ndevices); static char message[2000]; int length; int i; if (event->deviceid != device_info->deviceid) { unregister_events(display, event->deviceid); return; } strcpy(message, "InputButtonEvent "); strcat(message, identifier); length = strlen(message); length += sprintf(message + length, " num=%d state=%s", event->detail, (type == XI_RawButtonPress) ? "pressed" : "released"); int raw_value_index = 0; for (i = 0; i < device->num_classes; i++) { switch(device->classes[i]->type) { case XIButtonClass: break; case XIKeyClass: break; case XIValuatorClass: { XIValuatorClassInfo *v = (XIValuatorClassInfo*)(device->classes[i]); // length += sprintf(message + length, " %s=%.2f", valuators_names[v->number], v->value); length += sprintf(message + length, " %s=%.2f", valuators_names[v->number], event->raw_values[raw_value_index++]); break; } } } length += sprintf(message + length, " time=%u", (uint32_t)event->time); IvySendMsg(message); /* printf("%s\n", message);*/ XIFreeDeviceInfo(device); } static void raw_motion_callback (Display *display, XIRawEvent *event) { int i; // double *val; static char message[2000]; int length; if (event->deviceid != device_info->deviceid) { unregister_events(display, event->deviceid); return; } strcpy(message, "InputMoveEvent "); strcat(message, identifier); length = strlen(message); /* printf("device: %d, ", event->deviceid); printf("detail: %d, ", event->detail); printf("type: %d, ", event->evtype); printf("valuators:\n"); */ //val = event->valuators.values; int raw_value_index = 0; for (i = 0; i < event->valuators.mask_len * 8; i++) { if (XIMaskIsSet(event->valuators.mask, i)) { // length += sprintf(message + length, " %s=%.2f", valuators_names[i], *val++); length += sprintf(message + length, " %s=%.2f", valuators_names[i], event->raw_values[raw_value_index++]); } } length += sprintf(message + length, " time=%u", (uint32_t)(event->time)); IvySendMsg(message); /*printf("%s\n", message);*/ } static void X_callback (Channel channel, IVY_HANDLE fd, void *data) { static XEvent event; static XGenericEventCookie *cookie = (XGenericEventCookie*)&event.xcookie; while (XPending((Display *)data)) { /*while (XEventsQueued ((Display *)data, QueuedAlready) > 0) {*/ XNextEvent((Display *)data, &event); if (XGetEventData((Display *)data, cookie)) { if (cookie->type == GenericEvent && cookie->extension == xi_opcode) { switch (cookie->evtype) { case XI_HierarchyChanged: hierarchy_changed_callback(((XAnyEvent*)&event)->display, cookie->data); break; case XI_RawKeyPress: case XI_RawKeyRelease: printf("EVENT type %d (%s)\n", cookie->evtype, type_to_name(cookie->evtype)); break; case XI_RawButtonPress: case XI_RawButtonRelease: raw_button_callback(((XAnyEvent*)&event)->display, cookie->data, cookie->evtype); break; case XI_RawMotion: raw_motion_callback(((XAnyEvent*)&event)->display, cookie->data); break; case XI_RawTouchBegin: case XI_RawTouchUpdate: case XI_RawTouchEnd: printf("Touch: %s\n", type_to_name(cookie->evtype)); break; /* case XI_Enter: case XI_Leave: case XI_FocusIn: case XI_FocusOut: break; case XI_PropertyEvent: break; case XI_DeviceChanged: break;*/ default: printf("EVENT type %d (%s)\n", cookie->evtype, type_to_name(cookie->evtype)); break; } } XFreeEventData((Display *)data, cookie); } } } static void loop(Display *display) { /* MsgRcvPtr ptr;*/ char *ready_msg = malloc(strlen(application_name) + strlen(" Ready") + 1); sprintf(ready_msg, "%s Ready", application_name); IvyInit(application_name, ready_msg, NULL, NULL, NULL, NULL); /* ptr=IvyBindMsg(Callback,&ptr,REGEXP);*/ IvyStart(ivy_bus); IvyChannelAdd(ConnectionNumber(display), display, NULL, &X_callback, NULL); /* This code unlocks the IvyMainLoop (don't know why) */ while (XPending(display)) { XEvent Event; XNextEvent(display, &Event); } IvyMainLoop(); } static int xinput_version (Display *display) { XExtensionVersion *version; int result = -1; version = XGetExtensionVersion(display, INAME); if (version && (version != (XExtensionVersion*) NoSuchExtension)) { result = version->major_version; int major = version->major_version; int minor = version->minor_version; XIQueryVersion(display, &major, &minor); /* XIQueryVersion to request version 2.2 for multi touch */ XFree(version); } return result; } static int list_devices (Display *display) { int ndevices; int i; XIDeviceInfo *info, *dev; info = XIQueryDevice(display, XIAllDevices, &ndevices); for(i = 0; i < ndevices; i++) { dev = &info[i]; if (dev->use == XIMasterPointer) { printf("(master) id=%d\t%s\n", dev->deviceid, dev->name); } if (dev->use == XISlavePointer) { printf("(slave) id=%d\t%s\n", dev->deviceid, dev->name); } if (!dev->enabled) { printf("Device %d (%s) is disabled\n", dev->deviceid, dev->name); } } XIFreeDeviceInfo(info); return EXIT_SUCCESS; } /* static int print_single_device(char*def) { */ /* XIDeviceInfo *dev = xi2_find_device_info(display, def); */ /* if (!info) { */ /* fprintf(stderr, "unable to find device %s\n", def); */ /* return EXIT_FAILURE; */ /* } else { */ /* print_classes_xi2(display, dev->classes, dev->num_classes); */ /* return EXIT_SUCCESS; */ /* } */ /* } */ void usage () { printf("\nThis tool get data from xinput2 (X11 extension) and send them to\n" "applications using ivy bus.\n" "\n" "Options:\n" "\t-help:\t\t\tprint this help.\n\n" "\t-list:\t\t\tlist all pointer devices\n\n" "\t-application name:\tspecify the application name on the ivy bus\n" "\t\t\t\t(default is xinput2_ivy)\n\n" "\t-device id:\t\tinput device to use (can be string or number)\n\n" "\t-identifier id:\t\tidentifier used as prefix in ivy mesages\n" "\t\t\t\t(default is \"default\")\n\n" "\t-b [address]:port:\tspecify the ivy bus\n\n" "Sent messages:\n" "\tInputButtonEvent [identifier] num=[button id] state=[pressed|released] axis1name=value ... axisNname=value time=[time in ms]\n\n" "\tInputMoveEvent [identifier] axis1name=value ... axisNname=value time=[time in ms]\n\n"); } void parse_args (Display *display, int argc, char * argv[]) { int i; char *arg_name; for (i=1; i < argc; i++) { arg_name = argv[i]; if (strcmp(arg_name, "-list") == 0) { list_devices(display); } else if (strcmp(arg_name, "-b") == 0) { i++; ivy_bus = argv[i]; } else if (strcmp(arg_name, "-help") == 0) { usage(); } else if (strcmp(arg_name, "-application") == 0) { i++; application_name = argv[i]; } else if (strcmp(arg_name, "-device") == 0) { i++; device_name = argv[i]; } else if (strcmp(arg_name, "-identifier") == 0) { i++; identifier = argv[i]; } else { printf("\n*** Invalid option \"%s\".\n", arg_name); usage(); exit(EXIT_FAILURE); } } } int main (int argc, char * argv[]) { Display *display; int event, error; int xinput; /* Sanity checks */ display = XOpenDisplay(NULL); if (display == NULL) { fprintf(stderr, "Unable to connect to X server\n"); return EXIT_FAILURE; } if (!XQueryExtension(display, "XInputExtension", &xi_opcode, &event, &error)) { printf("X Input extension not available.\n"); return EXIT_FAILURE; } xinput = xinput_version(display); if (!xinput) { fprintf(stderr, "%s extension not available\n", INAME); return EXIT_FAILURE; } if (xinput != XI_2_Major) { fprintf(stderr, "%s extension version 2 not available\n", INAME); return EXIT_FAILURE; } if (argc != 1) { parse_args(display, argc, argv); } else { usage(); return EXIT_FAILURE; } if (device_name) { register_hierarchy_changed(display); device_info = find_device_info(display, device_name); if (device_info) { register_events(display, device_info); } else { printf("\n*** Unable to find device <<%s>> at starting time. Valid devices are:\n", device_name); list_devices(display); printf("\n*** Waiting for device <<%s>>.\n", device_name); } loop(display); } XSync(display, False); XCloseDisplay(display); return EXIT_SUCCESS; }