/* in_evdev.c general evdev input driver Copyright (C) 2021 Bill Currie Please see the file "AUTHORS" for a list of contributors This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include "QF/cvar.h" #include "QF/input.h" #include "QF/progs.h" // for PR_RESMAP #include "QF/sys.h" #include "QF/input/event.h" #include "compat.h" #include "qfselect.h" #include "evdev/inputlib.h" typedef struct devmap_s { struct devmap_s *next; struct devmap_s **prev; device_t *device; void *event_data; int devid; } devmap_t; static int evdev_driver_handle = -1; static int evdev_have_focus; static PR_RESMAP (devmap_t) devmap; static devmap_t *devmap_list; static void in_evdev_add_select (qf_fd_set *fdset, int *maxfd, void *data) { inputlib_add_select (&fdset->fdset, maxfd); } static void in_evdev_check_select (qf_fd_set *fdset, void *data) { inputlib_check_select (&fdset->fdset); } static void in_evdev_shutdown (void *data) { inputlib_close (); for (unsigned i = 0; i < devmap._size; i++) { free (devmap._map[i]); } free (devmap._map); } static void in_evdev_set_device_event_data (void *device, void *event_data, void *data) { device_t *dev = device; devmap_t *dm = dev->data; dm->event_data = event_data; } static void * in_evdev_get_device_event_data (void *device, void *data) { device_t *dev = device; devmap_t *dm = dev->data; return dm->event_data; } static void in_evdev_axis_event (axis_t *axis, void *_dm) { if (!evdev_have_focus) { return; } devmap_t *dm = _dm; //Sys_Printf ("in_evdev_axis_event: %d %d\n", axis->num, axis->value); IE_event_t event = { .type = ie_axis, .when = Sys_LongTime (), .axis = { .data = dm->event_data, .devid = dm->devid, .axis = axis->num, .value = axis->value, }, }; IE_Send_Event (&event); } static void in_evdev_button_event (button_t *button, void *_dm) { if (!evdev_have_focus) { return; } devmap_t *dm = _dm; //Sys_Printf ("in_evdev_button_event: %d %d\n", button->num, button->state); IE_event_t event = { .type = ie_button, .when = Sys_LongTime (), .button = { .data = dm->event_data, .devid = dm->devid, .button = button->num, .state = button->state, }, }; IE_Send_Event (&event); } static void device_add (device_t *dev) { const char *name = dev->name; // prefer device unique string if available, otherwise fall back to // the physical path const char *id = dev->uniq; if (!id || !*id) { id = dev->phys; } devmap_t *dm = PR_RESNEW (devmap); dm->next = devmap_list; dm->prev = &devmap_list; if (devmap_list) { devmap_list->prev = &dm->next; } devmap_list = dm; dev->data = dm; dev->axis_event = in_evdev_axis_event; dev->button_event = in_evdev_button_event; dm->device = dev; dm->devid = IN_AddDevice (evdev_driver_handle, dev, name, id); #if 0 Sys_Printf ("in_evdev: add %s\n", dev->path); Sys_Printf (" %s\n", dev->name); Sys_Printf (" %s\n", dev->phys); for (int i = 0; i < dev->num_axes; i++) { axis_t *axis = dev->axes + i; Sys_Printf ("axis: %d %d\n", axis->num, axis->value); } for (int i = 0; i < dev->num_buttons; i++) { button_t *button = dev->buttons + i; Sys_Printf ("button: %d %d\n", button->num, button->state); } #endif } static void device_remove (device_t *dev) { for (devmap_t *dm = devmap_list; dm; dm = dm->next) { if (dm->device == dev) { IN_RemoveDevice (dm->devid); if (dm->next) { dm->next->prev = dm->prev; } *dm->prev = dm->next; PR_RESFREE (devmap, dm); break; } } } static void in_evdev_init (void *data) { inputlib_init (device_add, device_remove); } static void in_evdev_clear_states (void *data) { } static void in_evdev_axis_info (void *data, void *device, in_axisinfo_t *axes, int *numaxes) { device_t *dev = device; if (!axes) { *numaxes = dev->num_axes; return; } if (*numaxes > dev->num_axes) { *numaxes = dev->num_axes; } for (int i = 0; i < *numaxes; i++) { axes[i].axis = dev->axes[i].num; axes[i].value = dev->axes[i].value; axes[i].min = dev->axes[i].min; axes[i].max = dev->axes[i].max; } } static void in_evdev_button_info (void *data, void *device, in_buttoninfo_t *buttons, int *numbuttons) { device_t *dev = device; if (!buttons) { *numbuttons = dev->num_buttons; return; } if (*numbuttons > dev->num_buttons) { *numbuttons = dev->num_buttons; } for (int i = 0; i < *numbuttons; i++) { buttons[i].button = dev->buttons[i].num; buttons[i].state = dev->buttons[i].state; } } static int in_evdev_get_axis_info (void *data, void *device, int axis_num, in_axisinfo_t *info) { device_t *dev = device; if (axis_num < 0 || axis_num > dev->num_axes) { return 0; } info->axis = dev->axes[axis_num].num; info->value = dev->axes[axis_num].value; info->min = dev->axes[axis_num].min; info->max = dev->axes[axis_num].max; return 1; } static int in_evdev_get_button_info (void *data, void *device, int button_num, in_buttoninfo_t *info) { device_t *dev = device; if (button_num < 0 || button_num > dev->num_buttons) { return 0; } info->button = dev->buttons[button_num].num; info->state = dev->buttons[button_num].state; return 1; } static in_driver_t in_evdev_driver = { .init = in_evdev_init, .shutdown = in_evdev_shutdown, .set_device_event_data = in_evdev_set_device_event_data, .get_device_event_data = in_evdev_get_device_event_data, .add_select = in_evdev_add_select, .check_select = in_evdev_check_select, .clear_states = in_evdev_clear_states, .axis_info = in_evdev_axis_info, .button_info = in_evdev_button_info, .get_axis_info = in_evdev_get_axis_info, .get_button_info = in_evdev_get_button_info, }; static int in_evdev_evend_handler (const IE_event_t *event, void *data) { if (event->type == ie_app_gain_focus) { evdev_have_focus = 1; return 1; } else if (event->type == ie_app_lose_focus) { evdev_have_focus = 0; return 1; } return 0; } static void __attribute__((constructor)) in_evdev_register_driver (void) { evdev_driver_handle = IN_RegisterDriver (&in_evdev_driver, 0); //FIXME probably shouldn't be here IE_Add_Handler (in_evdev_evend_handler, 0); } int in_evdev_force_link;