Created
September 12, 2019 06:56
-
-
Save jdinalt/f0ade7e8123d574e7d7c59e2efc9cd65 to your computer and use it in GitHub Desktop.
A quick hack to create a virtual "H-Shifter" from a numeric keypad
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <sys/fcntl.h> | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <errno.h> | |
#include <limits.h> | |
#include <assert.h> | |
#include <stdint.h> | |
#include <stdbool.h> | |
#include <linux/uinput.h> | |
#include <linux/input.h> | |
#include <linux/input-event-codes.h> | |
#include <libevdev/libevdev-uinput.h> | |
#include <libevdev/libevdev.h> | |
/* Define virtual shifter input codes */ | |
enum { | |
Key_first = KEY_INSERT, | |
Key_second = KEY_DELETE, | |
Key_third = KEY_HOME, | |
Key_fourth = KEY_END, | |
Key_fifth = KEY_PAGEUP, | |
Key_sixth = KEY_PAGEDOWN, | |
Key_seventh = KEY_PAUSE, | |
Key_reverse = KEY_BACKSPACE, | |
Key_neutral0 = KEY_BACKSLASH, | |
Key_neutral1 = KEY_BACKSLASH, | |
Key_neutral2 = KEY_BACKSLASH, | |
}; | |
/* Define virtual shifter output codes */ | |
enum { | |
Button_first = KEY_KP1, | |
Button_second = KEY_KP2, | |
Button_third = KEY_KP3, | |
Button_fourth = KEY_KP4, | |
Button_fifth = KEY_KP5, | |
Button_sixth = KEY_KP6, | |
Button_seventh = KEY_KP7, | |
Button_reverse = KEY_KP0, | |
}; | |
/* Declare types */ | |
typedef struct InputKB InputKB; | |
typedef struct VirtualKB VirtualKB; | |
typedef struct Event Event; | |
typedef struct Transition Transition; | |
typedef struct State State; | |
typedef struct Statemachine Statemachine; | |
/* Define types */ | |
struct VirtualKB { | |
int uifd; | |
int fd; | |
struct libevdev_uinput *uidev; | |
char const *syspath; | |
char const *devnode; | |
}; | |
struct InputKB { | |
int fd; | |
struct libevdev *dev; | |
int saved_repeat[2]; | |
}; | |
struct Event { | |
unsigned type; | |
unsigned code; | |
int value; | |
}; | |
struct Transition { | |
char const * name; | |
Event const * const * event_set; | |
State const * next_state; | |
Event const * const * actions; | |
bool local; | |
}; | |
struct State { | |
char const *name; | |
Transition const * const * transition_set; | |
Event const *const * entry_actions; | |
Event const * const * exit_actions; | |
}; | |
struct Statemachine { | |
char const *name; | |
State const *state; | |
VirtualKB *vkb; | |
}; | |
/* Helper macros for defining stuff */ | |
#define Event_DEFINE_EVENT(_name, _type, _code, _value) \ | |
Event const Event_ ## _name = { \ | |
.type = (_type), \ | |
.code = (_code), \ | |
.value = (_value), \ | |
} | |
#define Event_DEFINE_KEY_UP(_name, _code) \ | |
Event_DEFINE_EVENT(_name, EV_KEY, _code, 0) | |
#define Event_DEFINE_KEY_DOWN(_name, _code) \ | |
Event_DEFINE_EVENT(_name, EV_KEY, _code, 1) | |
#define Transition_DEFINE(_name, _event, _next_state) \ | |
Transition const Transition_ ## _name = { \ | |
.name = # _name, \ | |
.event_set = (Event const * const []) { \ | |
&Event_ ## _event, \ | |
NULL \ | |
}, \ | |
.next_state = &State_ ## _next_state, \ | |
} | |
/* Declare methods */ | |
int InputKB_ctor(InputKB* ikb, char const *dev_path); | |
void InputKB_dtor(InputKB* ikb); | |
int InputKB_nextEvent(InputKB* ikb, Event *event); | |
int VirtualKB_ctor(VirtualKB *vkb, const char *name, struct libevdev *dev, Event const * const *event_set); | |
void VirtualKB_dtor(VirtualKB *vkb); | |
int VirtualKB_postEvent(VirtualKB *vkb, Event const *event); | |
int VirtualKB_postEventSet(VirtualKB *vkb, Event const * const * event_set); | |
void Statemachine_ctor(Statemachine *fsm, char const *name, VirtualKB *vkb, | |
Transition const *initial_transition); | |
void Statemachine_dtor(Statemachine *fsm); | |
void Statemachine_transition(Statemachine *fsm, Transition const *trans); | |
void Statemachine_handleEvent(Statemachine *fsm, Event const *event); | |
void dump_dev_info(char const *prefix, struct libevdev *dev); | |
void Event_print(FILE *file, Event const *event); | |
/* Declare states */ | |
State const State_neutral; | |
State const State_first; | |
State const State_second; | |
State const State_third; | |
State const State_fourth; | |
State const State_fifth; | |
State const State_sixth; | |
State const State_seventh; | |
State const State_reverse; | |
/* Define Events */ | |
Event_DEFINE_KEY_DOWN(selectFirst, Key_first); | |
Event_DEFINE_KEY_DOWN(selectSecond, Key_second); | |
Event_DEFINE_KEY_DOWN(selectThird, Key_third); | |
Event_DEFINE_KEY_DOWN(selectFourth, Key_fourth); | |
Event_DEFINE_KEY_DOWN(selectFifth, Key_fifth); | |
Event_DEFINE_KEY_DOWN(selectSixth, Key_sixth); | |
Event_DEFINE_KEY_DOWN(selectSeventh, Key_seventh); | |
Event_DEFINE_KEY_DOWN(selectReverse, Key_reverse); | |
Event_DEFINE_KEY_DOWN(selectNeutral0, Key_neutral0); | |
Event_DEFINE_KEY_DOWN(selectNeutral1, Key_neutral1); | |
Event_DEFINE_KEY_DOWN(selectNeutral2, Key_neutral2); | |
Event_DEFINE_EVENT(syn, EV_SYN, SYN_REPORT, 0); | |
Event_DEFINE_KEY_DOWN(enterFirst, Button_first); | |
Event_DEFINE_KEY_UP(exitFirst, Button_first); | |
Event_DEFINE_KEY_DOWN(enterSecond, Button_second); | |
Event_DEFINE_KEY_UP(exitSecond, Button_second); | |
Event_DEFINE_KEY_DOWN(enterThird, Button_third); | |
Event_DEFINE_KEY_UP(exitThird, Button_third); | |
Event_DEFINE_KEY_DOWN(enterFourth, Button_fourth); | |
Event_DEFINE_KEY_UP(exitFourth, Button_fourth); | |
Event_DEFINE_KEY_DOWN(enterFifth, Button_fifth); | |
Event_DEFINE_KEY_UP(exitFifth, Button_fifth); | |
Event_DEFINE_KEY_DOWN(enterSixth, Button_sixth); | |
Event_DEFINE_KEY_UP(exitSixth, Button_sixth); | |
Event_DEFINE_KEY_DOWN(enterSeventh, Button_seventh); | |
Event_DEFINE_KEY_UP(exitSeventh, Button_seventh); | |
Event_DEFINE_KEY_DOWN(enterReverse, Button_reverse); | |
Event_DEFINE_KEY_UP(exitReverse, Button_reverse); | |
Event const * const Event_kInputSet [] = { | |
&Event_selectFirst, | |
&Event_selectSecond, | |
&Event_selectThird, | |
&Event_selectFourth, | |
&Event_selectFifth, | |
&Event_selectSixth, | |
&Event_selectSeventh, | |
&Event_selectReverse, | |
&Event_selectNeutral0, | |
&Event_selectNeutral1, | |
&Event_selectNeutral2, | |
NULL | |
}; | |
Event const * const Event_kOutputSet [] = { | |
&Event_enterFirst, | |
&Event_enterSecond, | |
&Event_enterThird, | |
&Event_enterFourth, | |
&Event_enterFifth, | |
&Event_enterSixth, | |
&Event_enterSeventh, | |
&Event_enterReverse, | |
NULL | |
}; | |
enum Event_Match { | |
Event_Match_TYPE = 0x01, | |
Event_Match_CODE = 0x02, | |
Event_Match_VALUE = 0x04, | |
Event_Match_ALL = Event_Match_TYPE | Event_Match_CODE | Event_Match_VALUE, | |
Event_Match_TYPE_CODE = Event_Match_TYPE | Event_Match_CODE, | |
}; | |
bool Event_isInSet(Event const *event, Event const *const * event_set, enum Event_Match match); | |
/* Declare initial transitions */ | |
Transition const Transition_initial; | |
void usage(int argc, char **argv) | |
{ | |
(void)argc; | |
fprintf(stdout, | |
"Usage: %s /dev/input/event<X>\n" | |
" /dev/input/event<X> : A keyboard input device\n" | |
"\n" | |
"Stack a virtual 'H-Shifter' interface onto a keyboard\n" | |
"\n" | |
"Shifters are expected to generate a 'key-down' event for \n" | |
"the selected 'gear' and not generate a 'key-up' event until the\n" | |
"taken out of gear. This makes using a normal keyboard for this \n" | |
"funcionality difficult, as it requires holding the key down \n" | |
"to stay in gear -- not unlike a broken transmission, but who \n" | |
"wants to emmulate driving a car with a broken transmission!?\n" | |
"\n" | |
"This takes the specified keyboard, grabs it for exclusive access, \n" | |
"creates a virtual keyboard, forwards normal key strokes, but \n" | |
"maps a H-shifter interface onto the numeric keypad.\n" | |
"\n" | |
" 1 3 5 \n" | |
" | | | \n" | |
" xxx---N------\n" | |
" | | | | |\n" | |
" R 2 4 6 7\n" | |
"\n" | |
" first --> KEY_KP7\n" | |
" second --> KEY_KP1\n" | |
" third --> KEY_KP8\n" | |
" fourth --> KEY_KP2\n" | |
" fifth --> KEY_KP9\n" | |
" sixth --> KEY_KP3\n" | |
" seventh--> KEY_KPDOT\n" | |
" reverse --> KEY_KP0\n" | |
" neutral --> KEY_KP4, KEY_KP5, KEY_KP6\n" | |
"\n" | |
"Entering and leaving reverse requires moving through neutral first\n" | |
"\n" | |
"As this driver disables key-repeat, it may fail if not run as root.\n" | |
"\n" | |
"See Also:\n" | |
" man 1 evtest\n" | |
" man 1 evemu-describe\n" | |
" https://www.kernel.org/doc/html/v4.12/input/input_uapi.html\n" | |
, | |
argv[0] | |
); | |
} | |
int | |
main(int argc, char **argv) | |
{ | |
(void)argc; | |
(void)argv; | |
InputKB ikb; | |
VirtualKB vkb; | |
Statemachine fsm; | |
int rc; | |
if (argc != 2) { | |
usage(argc, argv); | |
exit(1); | |
} | |
if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 ) { | |
usage(argc, argv); | |
exit(0); | |
} | |
/* Open the input keyboard */ | |
rc = InputKB_ctor(&ikb, argv[1]); | |
/* Construct our virtual keyboard */ | |
rc = VirtualKB_ctor(&vkb, "Virtual Shifter", ikb.dev, Event_kOutputSet); | |
if (rc < 0) { | |
fprintf(stderr, "Failed to create uinput device (%s)\n", strerror(-rc)); | |
exit(1); | |
} | |
/* Construct the shifter state-machine */ | |
Statemachine_ctor(&fsm, "shifter", &vkb, &Transition_initial); | |
while (true) { | |
Event event; | |
rc = InputKB_nextEvent(&ikb, &event); | |
switch (rc) { | |
case 0: | |
break; | |
case 1: | |
case -EAGAIN: | |
continue; | |
default: | |
fprintf(stderr, "Unexpected rc from libevdev_next_event \"%s\"; exiting\n", | |
strerror(-rc)); | |
exit(-rc); | |
} | |
/* Is this event in our reserved input set? */ | |
if (Event_isInSet(&event, Event_kInputSet, Event_Match_TYPE_CODE)) { | |
/* Let the state-machine handle it */ | |
Statemachine_handleEvent(&fsm, &event); | |
continue; | |
} | |
/* Forward, if not in our output set */ | |
if (!Event_isInSet(&event, Event_kOutputSet, Event_Match_TYPE_CODE)) { | |
VirtualKB_postEvent(&vkb, &event); | |
} | |
} | |
return 0; | |
} | |
void | |
dump_dev_info(char const *prefix, struct libevdev *dev) | |
{ | |
printf("%s name: \"%s\"\n", prefix, libevdev_get_name(dev)); | |
printf("%s ID: bus %#x vendor %#x product %#x\n", prefix, | |
libevdev_get_id_bustype(dev), | |
libevdev_get_id_vendor(dev), | |
libevdev_get_id_product(dev)); | |
} | |
void | |
Event_print(FILE* file, Event const *event) | |
{ | |
char const *type_name = libevdev_event_type_get_name(event->type); | |
char const *code_name = libevdev_event_code_get_name(event->type, event->code); | |
if (type_name) { | |
fprintf(file, "%s ", type_name); | |
} else { | |
fprintf(file, "%u ", event->type); | |
} | |
if (code_name) { | |
fprintf(file, "%s ", code_name); | |
} else { | |
fprintf(file, "%u ", event->code); | |
} | |
fprintf(file, "%d\n", event->value); | |
} | |
int | |
InputKB_ctor(InputKB* ikb, char const *dev_path) | |
{ | |
int rc; | |
ikb->fd = open(dev_path, O_RDWR | O_NONBLOCK); | |
if (ikb->fd < 0) { | |
rc = -errno; | |
fprintf(stderr, "Failed to open \"%s\" (%s)\n", dev_path, strerror(-rc)); | |
goto fail_open; | |
} | |
rc = libevdev_new_from_fd(ikb->fd, &ikb->dev); | |
if (rc < 0) { | |
fprintf(stderr, "Failed to create new input device (%s)\n", strerror(-rc)); | |
goto fail_new_dev; | |
} | |
if (!libevdev_has_event_type(ikb->dev, EV_KEY)) { | |
fprintf(stderr, "This device does not look like a keyboard\n"); | |
rc = EINVAL; | |
goto fail_type_check; | |
} | |
dump_dev_info("Input Device", ikb->dev); | |
rc = libevdev_grab(ikb->dev, LIBEVDEV_GRAB); | |
if (rc < 0) { | |
fprintf(stderr, "Failed to grab input device (%s)\n", strerror(-rc)); | |
goto fail_grab; | |
} | |
return 0; | |
fail_grab: | |
fail_type_check: | |
libevdev_free(ikb->dev); | |
fail_new_dev: | |
close(ikb->fd); | |
fail_open: | |
return rc; | |
} | |
void | |
InputKB_dtor(InputKB* ikb) | |
{ | |
libevdev_grab(ikb->dev, LIBEVDEV_UNGRAB); | |
libevdev_free(ikb->dev); | |
close(ikb->fd); | |
} | |
int | |
InputKB_nextEvent(InputKB* ikb, Event *event) | |
{ | |
int rc; | |
struct input_event ev; | |
rc = libevdev_next_event(ikb->dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); | |
if (rc) { | |
goto fail; | |
} | |
event->type = ev.type; | |
event->code = ev.code; | |
event->value = ev.value; | |
return 0; | |
fail: | |
return rc; | |
} | |
int | |
VirtualKB_ctor(VirtualKB *vkb, const char *name, struct libevdev *dev, Event const * const *event_set) | |
{ | |
int rc; | |
struct Event const *event; | |
unsigned repeat[2]; | |
/* TODO: Change name of new device */ | |
(void)name; | |
rc = libevdev_uinput_create_from_device(dev, LIBEVDEV_UINPUT_OPEN_MANAGED, &vkb->uidev); | |
if (rc) { | |
rc = -errno; | |
fprintf(stderr, "Failed libevdev_uinput_create_from_device (%s)\n", strerror(errno)); | |
goto fail_create; | |
} | |
/* for each event in 'event_set' ... */ | |
while ((event = *event_set++) != NULL) { | |
assert(event->type == EV_KEY); | |
printf("Registering Event: "); | |
Event_print(stdout, event); | |
/* Register event code */ | |
rc = libevdev_enable_event_code(dev, EV_KEY, event->code, NULL); | |
if (rc) { | |
rc = -errno; | |
fprintf(stderr, "Failed libevdev_enable_event_code() \"%s\"\n", strerror(errno)); | |
goto fail; | |
} | |
} | |
vkb->uifd = libevdev_uinput_get_fd(vkb->uidev); | |
vkb->syspath = libevdev_uinput_get_syspath(vkb->uidev); | |
vkb->devnode = libevdev_uinput_get_devnode(vkb->uidev); | |
printf("Device Node: %s\n", vkb->devnode ? vkb->devnode : "unknown"); | |
printf("Syspath: %s\n", vkb->syspath ? vkb->syspath : "unknown"); | |
if (!vkb->devnode) { | |
rc = -EINVAL; | |
fprintf(stderr, "Failed to get path for new device node\n"); | |
goto fail; | |
} | |
vkb->fd = open(vkb->devnode, O_RDWR | O_NONBLOCK); | |
if (vkb->fd < 0) { | |
rc = -errno; | |
fprintf(stderr, "Failed to open \"%s\" (%s)\n", vkb->devnode, strerror(-rc)); | |
goto fail; | |
} | |
rc = ioctl(vkb->fd, EVIOCGREP, &repeat[0]); | |
if (rc < 0) { | |
rc = -errno; | |
fprintf(stderr, "Failed EVIOCGREP \"%s\"\n", strerror(errno)); | |
goto fail_get_repeat; | |
} | |
printf("Repeat: delay=%u repeat=%u\n", repeat[0], repeat[1]); | |
printf("Disabling auto-repeat\n"); | |
/* Disable key repeat */ | |
repeat[1] = 0; | |
rc = ioctl(vkb->fd, EVIOCSREP, &repeat[0]); | |
if (rc < 0) { | |
rc = -errno; | |
fprintf(stderr, "Failed EVIOCSREP \"%s\"\n", strerror(errno)); | |
goto fail_set_repeat; | |
} | |
return 0; | |
fail_set_repeat: | |
fail_get_repeat: | |
close(vkb->fd); | |
fail: | |
libevdev_uinput_destroy(vkb->uidev); | |
fail_create: | |
return rc; | |
} | |
void | |
VirtualKB_dtor(VirtualKB *vkb) | |
{ | |
close(vkb->fd); | |
libevdev_uinput_destroy(vkb->uidev); | |
} | |
int | |
VirtualKB_postEvent(VirtualKB *vkb, Event const *event) | |
{ | |
int rc; | |
#if 0 | |
struct input_event ie = { | |
.type = event->type, | |
.code = event->code, | |
.value = event->value, | |
}; | |
printf("Posting Event: "); | |
Event_print(stdout, event); | |
#endif | |
rc = libevdev_uinput_write_event(vkb->uidev, event->type, event->code, event->value); | |
if (rc < 0) { | |
fprintf(stderr, "Failed write \"%s\"\n", strerror(-rc)); | |
goto fail_write; | |
} | |
return 0; | |
fail_write: | |
return rc; | |
} | |
int | |
VirtualKB_postEventSet(VirtualKB *vkb, Event const * const * event_set) | |
{ | |
Event const *event; | |
int rc; | |
if (!event_set) { | |
return 0; | |
} | |
while ((event = *event_set++) != NULL) { | |
rc = VirtualKB_postEvent(vkb, event); | |
if (rc) { | |
return rc; | |
} | |
} | |
return 0; | |
} | |
void | |
Statemachine_ctor(Statemachine *fsm, char const *name, VirtualKB *vkb, | |
Transition const *initial_transition) | |
{ | |
fsm->name = name; | |
fsm->vkb = vkb; | |
fsm->state = NULL; | |
Statemachine_transition(fsm, initial_transition); | |
} | |
void | |
Statemachine_dtor(Statemachine *fsm) | |
{ | |
Statemachine_transition(fsm, NULL); | |
} | |
void | |
Statemachine_transition(Statemachine *fsm, Transition const *trans) | |
{ | |
State const * from_state = fsm->state; | |
State const * to_state = trans->next_state; | |
/* | |
* If the from and to states are the same and this is not flagged | |
* as a local transition, then skip | |
*/ | |
if (from_state == to_state && !trans->local) { | |
return; | |
} | |
printf("%s: Executing transition \"%s\" %s => %s\n", | |
fsm->name, | |
trans->name, | |
from_state ? from_state->name : "initial", | |
to_state ? to_state->name : "final"); | |
/* If prior state, do exit actions */ | |
if (from_state) { | |
VirtualKB_postEventSet(fsm->vkb, from_state->exit_actions); | |
} | |
/* Set next state */ | |
fsm->state = to_state; | |
/* Do transition actions */ | |
VirtualKB_postEventSet(fsm->vkb, trans->actions); | |
/* Do entry actions for new state */ | |
if (to_state->entry_actions) { | |
VirtualKB_postEventSet(fsm->vkb, to_state->entry_actions); | |
} | |
} | |
void | |
Statemachine_handleEvent(Statemachine *fsm, Event const *event) | |
{ | |
/* If no state, just return */ | |
if (!fsm->state) { | |
return; | |
} | |
Transition const * const * transitions = fsm->state->transition_set; | |
Transition const * transition; | |
/* | |
* Determine if 'event' should trigger a state transition | |
* | |
* If 'event' matches the event in any event_set of a transition, then | |
* execute that state transition, else ignore event. | |
*/ | |
/* For each transition in the present state's transition set */ | |
while ((transition = *transitions++) != NULL) { | |
if (Event_isInSet(event, transition->event_set, Event_Match_ALL)) { | |
Statemachine_transition(fsm, transition); | |
return; | |
} | |
} | |
} | |
/* | |
* Define transitions | |
*/ | |
Transition const Transition_initial = { | |
.name = "initial", | |
.next_state = &State_neutral, | |
}; | |
Transition_DEFINE(toFirst, selectFirst, first); | |
Transition_DEFINE(toSecond, selectSecond, second); | |
Transition_DEFINE(toThird, selectThird, third); | |
Transition_DEFINE(toFourth, selectFourth, fourth); | |
Transition_DEFINE(toFifth, selectFifth, fifth); | |
Transition_DEFINE(toSixth, selectSixth, sixth); | |
Transition_DEFINE(toSeventh, selectSeventh, seventh); | |
Transition_DEFINE(toReverse, selectReverse, reverse); | |
Transition const Transition_toNeutral = { | |
.name = "toNeutral", | |
.event_set = (Event const * const []) { | |
&Event_selectNeutral0, | |
&Event_selectNeutral1, | |
&Event_selectNeutral2, | |
NULL | |
}, | |
.next_state = &State_neutral, | |
}; | |
#define Transition_FORWARD_TRANSITIONS \ | |
&Transition_toFirst, \ | |
&Transition_toSecond, \ | |
&Transition_toThird, \ | |
&Transition_toFourth, \ | |
&Transition_toFifth, \ | |
&Transition_toSixth, \ | |
&Transition_toSeventh \ | |
/* Define transition sets */ | |
Transition const * const forward_transitions [] = { | |
Transition_FORWARD_TRANSITIONS, | |
&Transition_toNeutral, | |
NULL | |
}; | |
/* Define reverse set */ | |
Transition const * const reverse_transitions [] = { | |
&Transition_toNeutral, | |
NULL | |
}; | |
/* | |
* Define states | |
*/ | |
#define State_DEFINE(_name, _entry, _exit, _trans) \ | |
State const State_ ## _name = { \ | |
.name = # _name, \ | |
\ | |
.transition_set = _trans, \ | |
.entry_actions = (Event const * const []) { \ | |
&Event_ ## _entry, \ | |
&Event_syn, \ | |
NULL \ | |
}, \ | |
.exit_actions = (Event const * const []) { \ | |
&Event_ ## _exit, \ | |
&Event_syn, \ | |
NULL \ | |
}, \ | |
} | |
State_DEFINE(first, enterFirst, exitFirst, forward_transitions); | |
State_DEFINE(second, enterSecond, exitSecond, forward_transitions); | |
State_DEFINE(third, enterThird, exitThird, forward_transitions); | |
State_DEFINE(fourth, enterFourth, exitFourth, forward_transitions); | |
State_DEFINE(fifth, enterFifth, exitFifth, forward_transitions); | |
State_DEFINE(sixth, enterSixth, exitSixth, forward_transitions); | |
State_DEFINE(seventh, enterSeventh, exitSeventh, forward_transitions); | |
State_DEFINE(reverse, enterReverse, exitReverse, reverse_transitions); | |
State const State_neutral = { | |
.name = "neutral", | |
.transition_set = (Transition const * const []) { | |
Transition_FORWARD_TRANSITIONS, | |
&Transition_toReverse, | |
NULL | |
}, | |
}; | |
bool | |
Event_isInSet(Event const *event, Event const *const * event_set, enum Event_Match match) | |
{ | |
Event const *e; | |
while ((e = *event_set++) != NULL) { | |
/* Does this event match the input? */ | |
if ( | |
(!(match & Event_Match_TYPE) || (e->type == event->type)) && | |
(!(match & Event_Match_CODE) || (e->code == event->code)) && | |
(!(match & Event_Match_VALUE) || (e->value == event->value))) { | |
return true; | |
} | |
} | |
return false; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment