The ve_device module provides a flexible model for input devices. Input devices and drivers are loaded at run-time either explicitly from a program or from a user-provided manifest. Input devices generate events which can then be filtered, through explicit structures or through filters loaded at run-time as part of the device settings. The event process model allows for the use of virtual devices - that is, remapping incoming events to arbitrary names that allow the application to be built independent of the actual input devices and to provide for the adoption of new input devices and methods with rebuilding the program.
Devices are represented as collections of elements. Each element represents a particular piece of input data. For example, a joystick may be divided into several elements - each button would be an element, an axis or a collection of axes would be another element, and so on. An element is uniquely identified by a pair of a device name and an element name.
Events that have a similar structure to elements are generated representing changes of state. These events are passed through any number of filters as determined by the system's filter table. Through a filtering, an event can be modified, renamed (i.e. its device and/or its element name changed), duplicated, or discarded.
Finally, events are delivered to the application's callbacks. Callbacks can be set to receive specific events or general collections of events.
Enumeration: VeDeviceEType
All elements and events use a type field There are several types of element data.typedef enum ve_device_etype { VE_ELEM_UNDEF = -1,
VE_ELEM_TRIGGER - A trigger is an element without state. It is used to indicate an event that occurs. For example, a vending machine would have a trigger to indicate that a quarter has been inserted. There is no state to report - simply that a quarter has been inserted.
VE_ELEM_TRIGGER = 0,
VE_ELEM_SWITCH - A switch is an element with two states - an active state and an inactive state. An obvious real-world example is a light switch which is either "on" or "off". Switch events occur when the switch transitions from one state to another. Other common examples include joystick and mouse buttons.
VE_ELEM_SWITCH,
VE_ELEM_VALUATOR - A valuator is an element with a range of possible values represented as a real number. A valuator may be either bounded, meaning that it has both a minimum and maximum value, or unbounded, meaning that any value is possible. Unbounded valuators are represented as having minimum and maximum values that are both 0.0. A throttle control or a steering wheel would be a valuator.
VE_ELEM_VALUATOR,
VE_ELEM_VECTOR - A vector is an array of valuators. The size of a vector is fixed throughout its lifetime. When a vector is created it may be given any size, but thereafter, that size remains fixed. Each valuator in the vector has an independent set of bounds. Unbounded and bounded valuators can be mixed within the same vector.
Vectors are typically used in cases where a set of valuators are interdependent or represent multi-dimensional data and reporting changes of individual valuators would be erroneous. For example, a head-tracker will typically have several dimensions of data, and any collection of changes in the data should be reported as a change to the head-tracker data as a whole. In other words, it would be wrong to report a change in the head's x-axis location and then a change in the head's y-axis location when the head had moved along a path which caused simultaneous displacements along both axes.
Filters can be used to isolate individual valuators within a vector if so desired.
VE_ELEM_VECTOR,
VE_ELEM_KEYBOARD - A keyboard is a special case of an input device. It is really a collection of switches. However, the set of switches is generally indefinite from the program's point of view. A keyboard is usually considered a device and its individual keys elements. Thus there are no keyboard "elements", but there are keyboard events. A keyboard event will represent both the state of the switch as well as including a code which identifies the key to which the event applies.
General practice is that the name of an element in a keyboard device corresponds to the key (if it has an ASCII representation) or the name of the key (e.g. Control_L or F1).
VE_ELEM_KEYBOARD} VeDeviceEType;
Structure: VeDeviceEContent
The actual content in an element or device is stored in a structure that is modelled after the VeDeviceEContent structure. All actual content structures have the fields defined in VeDeviceEContent in common, and these fields always appear first and in the same order as in VeDeviceEContent.In general, you only use the info in a VeDeviceEContent structure to figure out what it really is, and then cast to the type of structure that you really want to use. Note that a VeDeviceEContent structure is not guaranteed to be as large as real content structures, so you cannot pass VeDeviceEContent structures around by value - it must always be passed by reference.
typedef struct ve_device_econtent {
type - The type field determines what type of content we are actually dealing with.
VeDeviceEType type;} VeDeviceEContent;
Structure: VeDeviceE_Trigger
If the type of content isVE_ELEM_TRIGGERthen there is no effectively no content. For consistency this structure is defined for a trigger, but currently it contains no fields beyond the ones inVeDeviceEContent.typedef struct ve_device_e_trigger { VeDeviceEType type; } VeDeviceE_Trigger;
Structure: VeDeviceE_Switch
If the type of content isVE_ELEM_SWITCHthen this structure represents the real content.typedef struct ve_device_e_switch { VeDeviceEType type;
state - If non-zero, then the switch is in an "active" state. If zero, then the switch is in an "inactive" state. Most functions will limit the possible values to 1 and 0.
int state;} VeDeviceE_Switch;
Structure: VeDeviceE_Valuator
If the type of content isVE_ELEM_VALUATORthen this structure represents the real content.typedef struct ve_device_e_valuator { VeDeviceEType type;
min,max - These values represent the bounds of the valuator. If both values are 0.0 then the valuator is unbounded. A valuator either has both a minimum and a maximum or neither - there is no way to specify a only one of the two bounds.
float min,max;
value - The current value of the valuator. The VE library will generally constrain this value to be within the bounds (if defined) but these conditions may be violated by user-supplied filters. In cases where these limits are critical to the application, filters may be applied to clamp values.
float value;} VeDeviceE_Valuator;
Structure: VeDeviceE_Vector
If the type of content isVE_ELEM_VECTORthen this structure represents the real data.typedef struct ve_device_e_vector { VeDeviceEType type;
size - The size of the vector. A vector's size should not change after it has been created.
int size;
min,max - The bounds of the individual valuators. Both min and max are arrays of size size. For valuator n, min[n] represents the minimum bound, and max[n] represents the maximum bound. If both min[n] and max[n] are 0.0 then valuator i of the vector is unbounded.
float *min, *max;
value - The value of the individual valuators in the vector. This is an array of size size. The value of valuator n is value[n].
float *value;} VeDeviceE_Vector;
Structure: VeDeviceE_Keyboard
If the type of content isVE_ELEM_KEYBOARDthen this structure represents the real data. Note that this structure will never be used as an element - only as event data.typedef struct ve_device_e_keyboard { VeDeviceEType type;
key - The code for the key to which this data relates. Constants for key codes are defined in the
ve_keysym.hheader files. All input drivers which provide keyboards should conform to these pre-defined constants as much as possible to ensure consistency.int key;
state - As with a switch, a non-zero value indicates an "active" or "down" state for the key, and a zero value indicates an "inactive" or "up" state.
int state;} VeDeviceE_Keyboard;
Function: veDeviceEContentCreate
VeDeviceEContent *veDeviceEContentCreate(VeDeviceEType type, int vsize);Allocates memory for and initializes the given type of content.type - The type of content to create.
vsize - If the type of content being created is
VE_ELEM_VECTORthen this is the size of the vector to create. Otherwise this parameter is ignored.Returns: A pointer to the newly-allocated element content structure.
Function: veDeviceEContentCopy
VeDeviceEContent *veDeviceEContentCopy(VeDeviceEContent *c);Creates a duplicate of the given content. The copy does not share any memory with the original but contains the same information.c - The content to copy.
Returns: A newly-allocated element content structure which contains the same data as the original.
Function: veDeviceEContentDestroy
void veDeviceEContentDestroy(VeDeviceEContent *c);Frees a previously allocated veDeviceEContent structure. Note that this frees all memory associated with the element content.c - The element content structure to free.
Devices do not require device models, but without a model, the application has no information of the structure of a device.
Structure: VeDeviceElement
An element in a device model is a combination of a name (identifying the element) and element content.typedef struct ve_device_element {
name - The name of the element. Every element must have a name.
char *name;
content - A pointer to the element content structure for this element. The type of the element is defined in the content structure.
VeDeviceEContent *content;} VeDeviceElement;
Function: veDeviceElementCreate
VeDeviceElement *veDeviceElementCreate(char *name, VeDeviceEType type, int vsize);Creates an element. The created element is not associated with any model.name - The name of the element.
type - The type of the element content.
vsize - If the element content is of type
VE_ELEM_VECTORthen this parameter determines the size of the vector. Otherwise this parameter is ignored.Returns: A newly-allocated element.
Function: veDeviceParseElem
VeDeviceElement *veDeviceParseElem(char *spec);Creates an element based upon a string describing an element. A string description has the following format:[elem] name type [type_args ...]The word elem at the beginning is optional. If it is there it will be ignored. This means that it is a bad idea to call an element "elem". A name for the element and a type must be specified. Following the type may be initialization options for that particular type of element content. The following types and arguments are supported:For example:
- trigger
- A trigger has no arguments
- switch [state]
- A switch may optionally take an initial state for the switch. If the initial state is not specified, then it is left undefined.
- valuator [min [max [value]]]
- If no arguments are given to a valuator, then its minimum, maximum and value all default to 0.0. If the arguments are specified (as real numbers) then they are initialized appropriately.
- vector size [{ [min [max [value]]] }]
- A size must always be specified for a vector. Each valuator in the vector can have an initializer as for the "valuator" type, but it must be surrounded in curly braces: "{}".
elem foobar valuator -5.0 5.0would create an element called "foobar" as a valuator with a minimum bound of -5.0 and a maximum bound of 5.0. Since the value was not specified it defaults to 0.0.skippy vector 4 {-2.0 2.0} {} {0.0 0.0 40.0}creates a vector called "skippy" of size 4. The first valuator of the vector will have a minimum bound of -2.0 and a maximum bound of 2.0 and a value of 0.0 (default). The second valuator will be set to defaults (no bounds, value = 0.0). The third valuator is unbounded and has an initial value of 40.0. The fourth valuator's parameters are not specified so they are set to the defaults.spec - The string to parse.
Returns: A pointer to a newly-created element. If an error is encountered a
NULLpointer is returned.
Function: veDeviceElementDestroy
void veDeviceElementDestroy(VeDeviceElement *e);Destroys a previously allocated element, including its element content.e - The element to destroy.
Structure: VeDeviceModel
A device model is just a hash of names to VeDeviceElement pointers. Seeve_util.hfor more information on working with string maps. Convenience functions are provided so that you do not need to access the string map directly.typedef struct ve_device_model { VeStrMap elems; } VeDeviceModel;
Function: veDeviceCreateModel
VeDeviceModel *veDeviceCreateModel();Creates a new VeDeviceModel structure with an empty element map.Returns: A newly-allocated VeDeviceModel object.
Function: veDeviceAddElem
int veDeviceAddElem(VeDeviceModel *model, VeDeviceElement *elem);Adds an element to a device model. The element must have already been allocated and created. This does not create a copy of the element - the device model stores the given pointer. Thus the given element should not be freed after passing it to this function. Once an element is added to a device model, the device model will take care of freeing the memory associated with the element when necessary. If an element already exists in the device model with the name of the new element, the old element is removed and destroyed, and the new element is inserted in its place.model - The model to add the element to.
elem - The element to add.
Returns: 0 on success, non-zero on failure.
Function: veDeviceAddElemSpec
int veDeviceAddElemSpec(VeDeviceModel *model, char *spec);Adds an element to a device model given a string representation of that element. This in effect a convenience function forwith some added error checking.veDeviceAddElem(model,veDeviceParseElem(spec))model - The model to add the element to.
spec - The string describing the element in a form that is acceptable to
veDeviceParseElem().Returns: 0 on success, non-zero on failure.
Function: veDeviceFindElem
VeDeviceElement *veDeviceFindElem(VeDeviceModel *model, char *name);Looks up an element by name in a device model.model - The model to search in.
name - The name of the element you are looking for.
Returns: A pointer to the element if one exists in this model with the given name.
NULLotherwise.
A manifest also contains driver references, which let the system know what drivers need to be loaded for what types of devices. Driver references have general types - currently driver references for devices and filters are supported. Other driver references may be supported in the future.
There is a single device manifest in memory at run-time. All additions or removals are done to this global manifest.
Structure: VeDeviceDesc
A description of a device. Every description must include a name and a type. Names and types are just strings. As well, an arbitrary number of options can be defined for a device. These options are usually device specific (e.g. the x11 driver has a "display" option which allows you to specify which X server you want to use for input devices). These options are stored in a string map when the entries being strings. Strings in the options map are all allocated copies - that is, they have their own storage. If an option is removed from the string map, its value (i.e. the pointer to its contents) should be retrieved and freed first.typedef struct ve_device_desc {
name - The name of this device as a null-terminated string.
char *name;
type - The type of this device as a null-terminated string.
char *type;
options - Device-specific options as a string map. The value of an option is stored as string.
VeStrMap options;} VeDeviceDesc;
Function: veDeviceDescCreate
VeDeviceDesc *veDeviceDescCreate();Creates an empty device description.Returns: A newly-allocated VeDeviceDesc object.
Function: veDeviceDescDestroy
void veDeviceDescDestroy(VeDeviceDesc *desc);Frees memory allocated to a VeDeviceDesc object. This includes any strings in the options map as well as the object itself.desc - The description to free.
Function: veDeviceDescOption
char *veDeviceDescOption(VeDeviceDesc *desc, char *name);Retrieves an option from a description.desc - The description from which to retrieve the option.
name - The name of the option to retrieve.
Returns: The value of the option if it is defined,
NULLotherwise.
Function: veClearDeviceManifest
int veClearDeviceManifest();Clears the in-memory global device manifest. This does not affect any data on disk. After calling this function, the manifest will contain no data.Returns: 0 on success, non-zero on failure.
Function: veAddDeviceDesc
int veAddDeviceDesc(VeDeviceDesc *desc);Adds a device description to the global device manifest.desc - The description to add to the manifest.
Returns: 0 on success, non-zero on failure.
Function: veFindDeviceDesc
VeDeviceDesc *veFindDeviceDesc(char *name);Looks up an existing device description in the manifest by device name.name - The name of the device to locate.
Returns: A pointer to the description of the device if it exists,
NULLotherwise.
Function: veDeviceAddDriverRef
int veDeviceAddDriverRef(char *type, char *name, char *driverpath);Adds a driver reference to the manifest. Driver references are used to locate run-time loadable drivers for devices, filters, etc. A driver reference consists of a type (currently either "device" or "filter") and a name. For drivers, the name is the type of the device. That is, all devices of the same type share the same driver.type - The type of driver reference to add. This is a string. Currently valid values are device and filter.
name - The name of the specific object to add. For devices, this is the type of the device. For filters, this is the name of the filter.
driverpath - The path to the driver. This path will be searched using VE's usual rules for loading drivers (see
ve_driver.hfor driver details).
Function: veDeviceFindDriverRef
char *veDeviceFindDriverRef(char *type, char *name);Looks up a driver reference by driver type (device or filter) and name.type - The type of driver reference to find. This is a string. Currently valid values are device and filter.
name - The name of the specific object to look up. For devices, this is the type of the device. For filters, this is the name of the filter.
Returns: A string containing the path to the driver if it was found, or
NULLif it was not found. The returned string should be treated as static and should not be freed by the application.
Function: veReadDeviceManifest
int veReadDeviceManifest(FILE *stream, char *fname);Reads data from the given stdio stream and appends it to the global device manifest. This will overwrite any definitions that exist both in the stream and in the global manifest. Other definitions currently in the manifest will not be overwritten. If you wish to ensure that only those devices in the manifest are defined, callveClearDeviceManifest()before calling this function.The following entries can be found in a manifest file:
Blank lines and lines beginning with '#' are ignored.
- driver type name path - a driver reference.
- device name type [{options ...}] - a device description. Options are spread out over separate lines. On each option line, the first word is the name of the option and the remainder of the line is the value of the option.
- use name [ [type] {override-options ...}] - uses a device. Format is the same as a "device" line with the following changes. Both type and options may be omitted, but if "type" is included then options must also be specified. This means that there are three valid formats for the "use" statement:
use devicename- use the device with the options given in the "device" declaration.use devicename { newopts ... }- use the device with the options given in the "device" declaration but override those options with any values given in the option section of this "use" statement.use devicename devicetype { newopts ... }- define a new device and use it all in one line. The options section must be specified (even if it is empty) or "devicetype" will be interpreted as options (and it looks like the second form).- filter ... - a filter table entre (see
veDeviceParseFTableEntry()Here is an example:
# This is a device manifest # Entries can be in any order # - for x11keyboard type devices, load the x11drv.so driver driver device x11keyboard x11drv.so # - for x11mouse type devices, load the x11drv.so driver - VE knows # enough to only load the driver once driver device x11mouse x11drv.so # - declare a device called "keyboard" - since we omit options, # the defaults for that driver are used device keyboard x11mouse # - the following is a Flock of Birds head-tracker device headtrack fob { line /dev/ttya speed 115200 flow rtscts hemisphere forward }stream - The stdio stream from which to read the manifest.
fname - The name of the file from which data is being read. This is only used for informational purposes in error messages. If NULL, a fixed string will be substituted by the function.
Returns: 0 on success, non-zero on failure.
Function: veGetDeviceManifest
VeStrMap veGetDeviceManifest(void);Returns a string map containing all known names in the device manifest. The returned string map must not be altered by the calling program. The string map should only be used to determine the set of names in the device manifest - the data mapped to the string in the map should be left alone by the calling program.Returns: A pointer to the current device manifest or
NULLif there is currently no device manifest.
Be wary of the distinction between general drivers (i.e. shared objects and libraries) which are system objects, versus device drivers (module that handles input devices) which are VE objects.
A device in the system has up to two parts. One part is a model (as discussed earlier). The model is an abstract representation of a device. The other part is an instance, which represents an input device that will generate events. We group devices into two sets: real devices - that is, devices with instances that represent some source of input events for the program, and virtual devices - devices that do not generate input events, but which we may model, or refer to by name. The distinction is purely conceptual - the system itself makes no explicit distinction between virtual and real devices. Real devices always have an instance but may or may not have a model. Without a model, real devices can still generate input events, but the structure of the device cannot be inspected from the program. Virtual devices do not have an instance and may or may not have a model. Virtual devices are typically used for devices that are inherent to the application. For example, a driving simulator might have a virtual steering wheel, accelerator, brake, gear shift, etc. The real devices that control these may be joysticks, mice, keyboards, etc. The accelerator is virtual. If we provide a model for it, then that model can track changes made to its state by incoming events. However, even if we do not provide a model, we can still map incoming events to the accelerator. A device which has neither a model nor an instance is considered to be purely virtual. Purely virtual devices cannot be inspected but we can still receive and handle events mapped to these devices. The section below on events and filters describe some of the mechanisms for managing events and mapping events from one device to another.
Device drivers are expected to spawn any threads required to collect incoming data and generate events when a device is instantiated.
Structure: VeDeviceDriver
Entry in the internal driver table that maps a device type to the code that instantiates devices of that type. If a particular piece of driver code can support more than one type of device, then multiple VeDeviceDriver structures will need to be created.typedef struct ve_device_driver {
type - The type of device that this driver supports.
char *type;
instantiate - The function that will create the device. The arguments to the function will be a pointer to this driver structure as well as a device description. The function should return a pointer to e VeDevice structure (see below) on success, and
NULLon failure.struct ve_device *(*instantiate)(struct ve_device_driver *d,VeDeviceDesc *desc,VeStrMap override);} VeDeviceDriver;
Structure: VeDeviceInstance
An instance of a device consists of a reference to its driver, plus a private structure containing the instance-specific information.typedef struct ve_device_instance {
driver - A pointer to the driver structure from which this instance was spawned.
VeDeviceDriver *driver;
idata - The instance-specific data. This structure is private to the driver and should not be referenced from outside the driver.
void *idata;
options - A string map of options that are specific to this instance of this device.
VeStrMap options;} VeDeviceInstance;
Function: veDeviceInstanceInit
VeDeviceInstance *veDeviceInstanceInit(VeDeviceDriver *driver, void *idata, VeDeviceDesc *desc, VeStrMap override);Creates and initializes a device instance structure. This function call is provided as a convenience for device drivers.driver - The driver from which this device was created or
NULLif there is none.idata - The private (driver-specific) structure for this instance, or
NULLif there is none.desc - The description from which to initialize the option set, or
NULLif no description is provided.override - A set of options that override those in the description or
NULLif there are no override options.Returns: A pointer to a newly created instance structure if successful, or
NULLif an error occurred.
Function: veDeviceInstOption
char *veDeviceInstOption(VeDeviceInstance *i, char *name);Retrieves an option from an instance. Device driver writers should create an instance withveDeviceInstanceInit()and retrieve options withveDeviceInstOption()rather thanveDeviceDescOption().i - The instance from which to retrieve the option.
name - The name of the option to retrieve.
Returns: The value of the option if it is defined,
NULLotherwise.
Function: veDeviceAddDriver
int veDeviceAddDriver(VeDeviceDriver *d);Adds a device driver to the system table.d - The driver to add.
Returns: 0 on success, non-zero on failure.
Function: veDeviceFindDriver
VeDeviceDriver *veDeviceFindDriver(char *type);Finds a device driver in the system table for the given device type.type - The type of device for which we want to find a driver.
Returns: A pointer to a VeDeviceDriver structure if successful,
NULLif a driver cannot be found.
Structure: VeDevice
An actual device. Only those devices with either an instance or model should have a VeDevice structure associated with them. It is possible to create a purely virtual device structure (with null entries for the instance and the model) but this structure does not serve any purpose.typedef struct ve_device {
name - The name of the device (from the device description used to generate this device).
char *name;
instance - A pointer to the instance of this device. Virtual devices will have a null value here.
VeDeviceInstance *instance;
model - A pointer to the model for this device. This will be a null value if the device has no model.
VeDeviceModel *model;} VeDevice;
Function: veDeviceCreate
VeDevice *veDeviceCreate(char *name);Creates a new device. The structure will initially be purely virtual in that the instance and model will not be defined. This function just allocates the structure - it does not attempt to build all of the aspects of the device object. SeeveDeviceUse()for this.name - The name of the device to create.
Returns: A pointer to a newly-allocated VeDevice object.
Function: veDeviceCreateVirtual
VeDevice *veDeviceCreateVirtual(char *name, VeDeviceModel *model);Creates a new virtual device. This device will be locatable usingveDeviceFind()and if it has a model then incoming events will be applied to it.name - The name of the device.
model - An optional model for the device. If this is
NULLthen a purely virtual device is created (i.e. without a model).Returns: A pointer to the newly-created VeDevice object or
NULLif there is an error or if a device with that name already exists.
Function: veDeviceUseByDesc
VeDevice *veDeviceUseByDesc(VeDeviceDesc *desc, VeStrMap override);Creates a device based upon a device description. This will also load any drivers the device may need as defined by driver references. If the driver has been previously created via aveDeviceUseByDesc()orveDeviceUse()call, then a new device will not be created and a pointer to the old device will be returned. The device will be instantiated if necessary.desc - A pointer to the device description.
Returns: A pointer to the device. If the device is not in use, then the device will be instantiated.
NULLis returned in the event of an error.
Function: veDeviceUse
VeDevice *veDeviceUse(char *name, VeStrMap override);Creates a device based upon a device description from the global device manifest. This is the command that is typically used for instantiating devices. Its behaviour is the same asveDeviceUseByDesc()except that the device description comes from the global manifest rather than being explicitly given as a parameter.name - The name of the device to use.
Returns: A pointer to the device. If the device is not in use, then the device will be instantiated.
NULLis returned in the event of an error, including the case if the device cannot be found in the manifest.
Function: veDeviceFind
VeDevice *veDeviceFind(char *name);Finds the device structures of active devices. This will not instantiate a device. Only devices that have been previously used can be retrieved with this function. Note that purely virtual devices do not exist as active devices and thus cannot be found by this command.name - The name of the device to find.
Returns: A pointer to the device if it has been previously used,
NULLif the device is not in use.
Structure: VeDeviceEvent
The object that represents a single event. A number of macros are provided to simplify working with event structures. All arguments to these macros are pointers to VeDeviceEvent objects.
VE_EVENT_TYPE(x)- Returns the type of the event pointed to by
x.VE_EVENT_KEYBOARD(x)- Returns a pointer to a VeDeviceE_Keyboard structure which is the event's element content. If the type of the event is not
VE_ELEM_KEYBOARDthen the result of this macro is undefined.VE_EVENT_TRIGGER(x)- Returns a pointer to a VeDeviceE_Trigger structure which is the event's element content. If the type of the event is not
VE_ELEM_TRIGGERthen the result of this macro is undefined.VE_EVENT_SWITCH(x)- Returns a pointer to a VeDeviceE_Switch structure which is the event's element content. If the type of the event is not
VE_ELEM_SWITCHthen the result of this macro is undefined.VE_EVENT_VALUATOR(x)- Returns a pointer to a VeDeviceE_Valuator structure which is the event's element content. If the type of the event is not
VE_ELEM_VALUATORthen the result of this macro is undefined.VE_EVENT_VECTOR(x)- Returns a pointer to a VeDeviceE_Vector structure which is the event's element content. If the type of the event is not
VE_ELEM_VECTORthen the result of this macro is undefined.typedef struct ve_device_event {
timestamp - The time at which this event occurred. This time is typically the value of
veClock()at the time the event occurred. Its units are milliseconds and the time is relative to the clock's zero reference point.long timestamp;
device - The name of the device from which this event originates. This may be a real, virtual or purely virtual device. This name may be modified by filters before being passed to callbacks.
char *device;
elem - The name of the element from which this event originates. This element may or may not be a declared member of the device. In other words, there are no restrictions on what this value may be.
char *elem;
index - Filters and callbacks may operate on a specific valuator in a vector. In those cases, this field is used to indicate which valuator in the vector to use.
int index;
content - The actual data (i.e. element content) for the event.
VeDeviceEContent *content;} VeDeviceEvent;
Function: veDeviceEventCreate
VeDeviceEvent *veDeviceEventCreate(VeDeviceEType type, int vsize);Creates a device event object.type - The type of the event to create.
vsize - If the event is of type
VE_ELEM_VECTORthen this argument is the size of the vector.Returns: A pointer to the newly created object, or
NULLif an error occurs.
Function: veDeviceEventInit
VeDeviceEvent *veDeviceEventInit(VeDeviceEType type, int vsize, char *device, char *elem);Creates a device event object and initializes some fields. Note that the callveDeviceEventInit(type,vsize,NULL,NULL)is not equivalent toveDeviceEventCreate(type,vsize). Some fields (e.g. timestamp) are implicitly initialized (e.g. with the current time).type - The type of the event to create.
vsize - If the event is of type
VE_ELEM_VECTORthen this argument is the size of the vector.device - The name of the device from which this event originates, or
NULLto not initialize the device field.elem - The name of the element from which this event originates, or
NULLto not initialize the event field.Returns: A pointer to the newly created object, or
NULLif an error occurs.
Function: veDeviceEventCopy
VeDeviceEvent *veDeviceEventCopy(VeDeviceEvent *e);Creates a copy of an event. The new event shares no memory with the old event - i.e. you can modify or destroy the old event without affecting the new event and vice versa.e - The event to copy.
Returns: A duplicate of the given event.
Function: veDeviceEventDestroy
void veDeviceEventDestroy(VeDeviceEvent *e);Destroys an event object and frees any memory associated with it.e - The event to destroy.
Function: veDeviceEventFromElem
VeDeviceEvent *veDeviceEventFromElem(char *device, VeDeviceElement *el);Creates an event based upon an element structure. The device name must also be specified. Other fields (e.g. timestamp) are filled in the same way asveDeviceEventInit().device - The name of the device to use, or the
NULLto leave the device field of the event unspecified.Returns: A pointer to the newly created object, or
NULLif an error occurs.
Function: veDeviceApplyEventToModel
void veDeviceApplyEventToModel(VeDeviceModel *m, VeDeviceEvent *e);Updates a device model based upon the given event. This function does not consider the device name in the event, but the element named in the event must exist in the device model, otherwise the function has no effect. If the element does exist in the model, then its state is updated to match the state information in the event's element content.m - The model to update.
e - The event which is used to update the model.
Function: veDeviceApplyEvent
void veDeviceApplyEvent(VeDeviceEvent *e);Applies an event to a matching device if that device has a model associated with it. In effect, this function looks up a device based upon the device name in the event and then usesveDeviceApplyEventToModel()to update the device model. For this function to update a device, the device must have a corresponding VeDevice object.e - The event to apply.
All callbacks must be of type VeDeviceEventProc which
is a function which takes two arguments: a pointer to the event
which is being passed to it, and an abritrary argument which should
be supplied by the application when setting up the callback.
Although events are generally consumed by the first callback they are passed to, there is one exception. If a callback matches one valuator of a vector, then callback processing will continue after handling that callback. Callbacks can abort any further processing by returning a non-zero value.
Callbacks are passed pointers to events whose memory is managed elsewhere. They should not destroy these events.
Structure: VeDeviceSpec
For both callbacks and filters, events are selected based upon a device specification. A specification has three parts - a device part, an element part, and an index part. The device part defines which devices match the specification. It can either be a specific device or "*" which is a wildcard meaning that any device matches the device part. The device part can be omitted which is equivalent to specifying "*". The element part defines which elements match the specification. Like the device part, it is either a specific name or "*" denoting any element. However, specific strings can match either an element's name or an element's type. For example, an element part of "foo" would match an element with a name of "foo" and would also match an element of type "foo". Types are given obvious names ("trigger", "switch", "valuator", "vector", "keyboard"). The index part is only meaningful if the type of element being matched is a vector, otherwise it is ignored. If specified, it must be an integer denoting a specific valuator in a vector.See
veDeviceParseSpec()for how to express specifications as a string.typedef struct ve_device_spec {
device - The device part of the specification. If NULL, then the device part is not specified.
char *device;
elem - The element part of the specification. If NULL, then the element part is not specified.
char *elem;
index - If this value is greater then or equal to 0 then it matches a specific valuator of a vector. If this is less than 0 or if the element type being matched is not a vector then this field is ignored.
int index;} VeDeviceSpec;
Function: veDeviceSpecCreate
VeDeviceSpec *veDeviceSpecCreate();Creates a new empty specification. By default the specification is all unspecified, meaning that it is equivalent to wildcards.Returns: A pointer to a newly created VeDeviceSpec object.
Function: veDeviceSpecDestroy
void veDeviceSpecDestroy(VeDeviceSpec *s);Destroys a previously created device specification and frees any memory associated with it.s - The specification to destroy.
Function: veDeviceParseSpec
VeDeviceSpec *veDeviceParseSpec(char *str);Parses a device specification given as a string. The three parts are given in order separated by periods:device.elem.indexAny part can be omitted. If trailing parts (e.g. index, or elem and index) are omitted, then the periods preceeding them must also be omitted. If a part is omitted is left unspecified in the structure. For devices and elements this leaves a null field and for the index this sets the value to -1. For example:
- foobar
- foobar.*
- Matches any element of any device called foobar. Both cases are equivalent.
- *.thingy
- Matches any element called "thingy" or of type "thingy" in any device.
- foobar.thingy.4
- Matches the valuator with index 4 of device foobar and element name/type thingy. If the event being matched is not for a vector, then the index portion is ignored.
str - The string containg the specification to parse.
Returns: A pointer to a newly-created device specification or
NULLif an error is encountered.
Function: veDeviceMatchSpec
int veDeviceMatchSpec(VeDeviceEvent *e, VeDeviceSpec *s);Compares an event to a device specification.e - The event.
s - The specification.
Returns: A boolean value. True (non-zero) if the event matches the specification nad false (zero) otherwise.
Function: veDeviceAddCallbackSpec
int veDeviceAddCallbackSpec(VeDeviceEventProc p, void *arg, VeDeviceSpec *s);Adds a callback to the internal list of callbacks given a device specification object.p - The callback to add.
arg - An application-supplied argument that will be passed to the callback when it is called due to an event matching the given specification. The library does not interpret this argument at all - it is provided for applications to pass some context to callbacks.
s - A device specification object defining which events should be passed to this callback.
Returns: 0 on success, non-zero on failure.
Function: veDeviceAddCallback
int veDeviceAddCallback(VeDeviceEventProc p, void *arg, char *spec);Adds a callback to the internal list of callbacks given a device specification as a string. This is the more common function for adding callbacks.p - The callback to add.
arg - An application-supplied argument that will be passed to the callback when it is called due to an event matching the given specification. The library does not interpret this argument at all - it is provided for applications to pass some context to callbacks.
spec - A device specification as a string suitable for
veDeviceParseSpec().Returns: 0 on success, non-zero on failure.
Function: veDeviceRemoveCallback
int veDeviceRemoveCallback(VeDeviceEventProc p);Removes a callback previously added to the internal list. All callbacks that call the given function will be removed.p - All callbacks that would call this function will be removed from the internal list.
Returns: 0 on success, non-zero on failure.
Function: veDeviceHandleCallback
int veDeviceHandleCallback(VeDeviceEvent *e);Processes callbacks for an event. This function will call any callbacks that are appropriate for the given event based upon the internal list of callbacks. If a callback returns a non-zero value then any further processing is aborted.e - The event to pass to callbacks.
Returns: The result of the last callback called.
A filter is in effect a callback that is passed an event. Filters are arranged sequentially in a filter table. Normally an event is applied to each filter in the table in turn. The return value of the result determines the result of the filter and affects the disposition of the library towards that event:
A filter may have many instances, each with different parameters and each handling a different set of events. Filters are in effect factories which generate instances. Instances of filters do the actual processing. When instantiated, filters can take arguments of two kinds - positional arguments that are passed in an array in the order they appear and named arguments which are passed as a string map pointing to strings (see ve_util.h). The specific use of these arguments is up to the specific filter implementation.
Structure: VeDeviceFilter
This objects defines the general properties of a filter and allows it to be instantiated. Instances of filter share the same processing code but may have their own arguments, settings.typedef struct ve_device_filter {
name - The name of this filter.
char *name;
proc - A pointer to the function containing the actual execution code for this filter. The format of proc is the same as a callback function.
VeDeviceEventProc proc;
filtarg - A generic argument that filter implementations can use to share an instantiation function among different filters. The library makes no use of this argument - it is reserved for filter implementations.
void *filtarg;
inst - The instantiation function. All filters must provide this function as a method of creating filter instances. The arguments include a pointer to the filter object (this one), and a set of positional arguments (defined by args and nargs) and a set of named arguments given as a reference to a string map. The function should return a pointer to a new VeDeviceFilterInst structure representing the new filter instance.
struct ve_device_filter_inst *(*inst)(struct ve_device_filter *filt, char **args, int nargs, VeStrMap optargs);
deinst - A function that will destroy an instance previously created by the inst function. If this is
NULLthen the library will use its default action, which is just to free the instance object.void (*deinst)(struct ve_device_filter_inst *);} VeDeviceFilter;
Function: veDeviceAddFilter
int veDeviceAddFilter(VeDeviceFilter *filter);Adds a filter to the program's set of available filters. Note that this is not the table of filter instances which will be applied to events, but the set of filters which may be instantiated. If a filter exists in the system with the same name, the previous filter will be replaced with this one.filter - The filter to add.
Returns: 0 on success, non-zero on failure.
Function: veDeviceFindFilter
VeDeviceFilter *veDeviceFindFilter(char *name);Finds a filter by name in the program's set of available filters.name - The name of the filter to find.
Returns: A pointer to the filter object if it exists or
NULLif the filter does not exist.
Structure: VeDeviceFilterInst
An instance of a filter.typedef struct ve_device_filter_inst {
filter - A pointer to the filter object from which this one was instantiated. There are special cases of filters where this argument is equivalent to
NULL. See the special member below.VeDeviceFilter *filter;
arg - This argument is passed to the filter processing function. This is generally chosen by the instantiating function of the filter and typically points to internal state information for the filter. The library does not interpret this argument at all but merely passes it to the filter processing function.
void *arg;
special - If the filter member is
NULLthen this field determines what this filter does. This value should be the return code (VE_FILT_CONTINUE,VE_FILT_RESTART, etc.) which should be returned as the result of this filter. Note that for a special filter like this, there is no actual processing done.int special;
next - This field is used to point to next filter instance in a chain. See the section on the filter table below.
struct ve_device_filter_inst *next;} VeDeviceFilterInst;
Function: veDeviceCreateFilterInst
VeDeviceFilterInst *veDeviceCreateFilterInst();Creates a new filter instance. This creates an empty object.Returns: A pointer to a newly-created filter instance object.
Function: veDeviceDestroyFilterInst
void veDeviceDestroyFilterInst(VeDeviceFilterInst *inst);Frees memory associated with a filter instance. If a filter provides a deinst function then that will be used to destroy the instance. Otherwise, the instance is just freed.inst - The instance to destroy.
Function: veDeviceParseFilterDesc
VeDeviceFilterInst *veDeviceParseFilterDesc(char *fdesc);Creates a filter instance based upon a string description of the filter instance. A filter instance string has the following format:name (parg|name=value) ...The first argument is always the name of the filter. Every word after that is a named argument if it contains an "=" character, and otherwise a positional parameter. For example,rename newdev.buttonhas a single positional argument, andto_valuator expr=1.0*x min=0.0 max=1.0has three named arguments. Named arguments and positional arguments can be mixed, e.g.junk a b v=199 c dThere are three special names which are reserved. If the name of the filter in the description is discard, deliver, restart, or exit then a special filter is created with the appropriate disposition. Other arguments in the description are ignored.
fdesc - The string containing the filter instance description.
Returns: A pointer to a newly-created filter instance corresponding to the description if successful,
NULLon error.
Structure: VeDeviceFTableEntry
An entry in the filter table representing a filter chain.typedef struct ve_device_ftable_entry {
spec - The device specification describing what events this filter chain should process.
VeDeviceSpec *spec;
head - The start of the filter chain. The next member of the VeDeviceFilterInst structure is used to point to the next filter instance in the chain.
VeDeviceFilterInst *head;
next - Points to the next filter table entry (not the next filter in the chain).
struct ve_device_ftable_entry *next;} VeDeviceFTableEntry;
Function: veDeviceRunFilterChain
int veDeviceRunFilterChain(VeDeviceFilterInst *head, VeDeviceEvent *e);Processes a filter chain. Applies each filter instance in the chain in turn to the given event.head - The head of the filter chain to apply.
e - The event to process.
Returns: The status of the last filter applied. This may not be the last filter in the chain if a filter instance returns a result other than "continue".
Function: veDeviceCreateFTableEntry
VeDeviceFTableEntry *veDeviceCreateFTableEntry();Creates a new filter table entry object.Returns: A pointer to a newly-created filter table entry object.
Function: veDeviceDestroyFTableEntry
void veDeviceDestroyFTableEntry(VeDeviceFTableEntry *e);Frees memoery associated with a previously-created filter table entry object.e - The object to free.
Function: veDeviceParseFTableEntry
VeDeviceFTableEntry *veDeviceParseFTableEntry(char *desc);The most convenient way of creating filter table entries is to parse them from a string. An entry has the following structure:[filter] spec {flist}The entry can optionally begin with the word "filter" - if it is present, then the word "filter" is discarded. A device specification is followed by a list of filter descriptions. The list of filter descriptions should have at most one filter description per line. Blank lines and lines beginning with '#' in the filter list are ignored. For example:filter sidewinder.button2 { # fake a valuator to_valuator expr=1.0*x min=0.0 max=1.0 rename location.x }This creates a filter chain that will affect all events matching the device specification "sidewinder.button2". Events that match will be passed to the "to_valuator" filter and then the "rename" filter with the given arguments.desc - The description to parse.
Returns: A pointer to a newly-created fitler table entry if successful or
NULLif an error occurs.
Structure: VeDeviceFTable
All filter table entries are kept in a global filter table.
The entries are arranged in a linear list.
typedef struct ve_device_ftable {
VeDeviceFTableEntry *head, *tail;
} VeDeviceFTable;
Function: veDeviceCreateFTable
VeDeviceFTable *veDeviceCreateFTable();Creates an empty filter table object. This function is currently only used internally within the library.Returns: A newly-created filter table object.
Function: veDeviceDestroyFTable
void veDeviceDestroyFTable(VeDeviceFTable *f);Frees memory associated with a previously-created filter table object. This will also free any filter table entry objects in the table.f - The table to destroy.
Function: veDeviceFTableAdd
int veDeviceFTableAdd(VeDeviceFTableEntry *e, int where);Adds a filter table entry to the global table.e - The filter table entry to add. This pointer will be stored in the table so the structure it points to should be left alone by the program.
where - If where is
VE_FTABLE_HEADthen the entry is inserted at the beginning of the table. If where isVE_FTABLE_TAILthen the entry is inserted at the end of the table. Other values for this parameter are undefined.Returns: 0 on success, non-zero on error.
Function: veDeviceFTableAddDesc
int veDeviceFTableAddDesc(char *desc, int where);Adds an entry to the filter table based upon a string describing the filter. This is in effect shorthand for:with some extra error checking.veDeviceFTableAdd(veDeviceParseFTableEntry(desc),where)desc - The description of the filter table entry as would be acceptable to
veDeviceParseFTableEntry().where - If where is
VE_FTABLE_HEADthen the entry is inserted at the beginning of the table. If where isVE_FTABLE_TAILthen the entry is inserted at the end of the table. Other values for this parameter are undefined.Returns: 0 on success, non-zero on error.
Function: veDeviceFTableFind
VeDeviceFTableEntry *veDeviceFTableFind(VeDeviceEvent *e, VeDeviceFTableEntry *last);Finds the next filter table entry in the global table that matches the given event.e - The event to match.
last - If
NULLthen the search begins at the beginning of the table. Otherwise, the search begins with the entry following this one.Returns: A pointer to a matching table entry if there is one,
NULLotherwise.
Function: veDevicePushEvent
int veDevicePushEvent(VeDeviceEvent *e, int where);Adds an event to the event queue. This function is thread-safe.e - The event to add. This pointer will be added to the queue so it should not refer to volatile memory. The library will take care of freeing resources related to the event at a later point.
where - Where to add the event. If this is
VE_QUEUE_HEADthen the event is added to the head of the queue. If this isVE_QUEUE_TAILthen it is added to the tail. Other values for this parameter are undefined.Returns: 0 on success, non-zero on failure.
Function: veDeviceEventPending
int veDeviceEventPending();Checks to see if there are any events in the queue.Returns: 0 if there are no events in the queue, 1 if there are events available.
Function: veDeviceNextEvent
VeDeviceEvent *veDeviceNextEvent(int where);Retrieves the next event in the queue (if any).where - Where to retrieve the event from. If this is
VE_QUEUE_HEADthen the event is retrieved from the head of the queue. If this isVE_QUEUE_TAILthen it is retrieved from the tail. Other values for this parameter are undefined.Returns: A pointer to an event object if there are events in the queue.
NULLis returned if the queue is empty. The returned event is removed from the queue - it is up to the calling program to destroy the event when it has finished with it.
Function: veDeviceWaitForEvents
void veDeviceWaitForEvents();Puts this thread to sleep until there are events in the queue.
Function: veDeviceProcessEvent
int veDeviceProcessEvent(VeDeviceEvent *e);Processes an event. This entails applying all applicable filter chains in the filter table, applying the event to a device model (if applicable) and then passing it to any existing callbacks. Only one event may be processed at a time and other threads that attempt to process events while this thread is processing will be blocked.Note that there are special circumstances for vectors that get processed through a filter chain which selects a specific index of the vector. In this case, the filter only applies to that valuator and not the whole vector. A new event of type valuator is created to be filtered. If that event does not change its type, device or element, then the new filtered content is merged back into the vector and the new event is destroyed. Otherwise if any of the type, device or element names change, and the result is not merged back into the vector and the filtered event is treated as a new valuator event separate from the original vector event. In this case (where a particular index of a vector is selected) the result of the filter only ever applies to the new valuator event and not to the vector event (which always continues).
e - The event to process.
Returns: 0 on success, non-zero on error, either internal or from a filter (i.e.
VE_FILT_ERRORstatus).
Function: veDeviceInsertEvent
int veDeviceInsertEvent(VeDeviceEvent *e);Similar toveDeviceProcessEvent()but meant as a hook for devices to deliver events to the system. If there are no events in the queue, then this will attempt to process the event immediately. Otherwise the event will be queued for later processing.e - The event to insert.
Returns: 0 on success, non-zero on error, either internal or from a filter (i.e.
VE_FILT_ERRORstatus). Note that if the event needs to be queued for later the result will be 0 and any filter errors will be delivered to the thread that processes the event from the queue.
Function: veDeviceBlockEvents
void veDeviceBlockEvents(int disp);Prevents events from being dispatched to handlers. No events will be processed untilveDeviceUnblockEvents()is called.disp - The disposition towards events. The following constants are defined and recognized:
VE_DEVICE_DISCARD- any blocked events should be discarded.VE_DEVICE_QUEUE- any blocked events should be queued and will be processed once the block is lifted.
Function: veDeviceUnblockEvents
void veDeviceUnblockEvents(void);Removes a previous block on events. If the disposition towards blocked events was to queue them, then when the event queue handler next runs, all blocked events will be processed. If the disposition towards blocked events was to discard them, then all events that were blocked are lost, and only events generated after the block is lifted will be processed. If no block currently exists, then this callback has no effect.
Function: veDeviceInit
int veDeviceInit();This function must be called before calling any other ve_device functions. It is generally called byveInit().
Function: veDeviceDumpFTable
void veDeviceDumpFtable(void);This function is strictly for debugging purposes. It prints the contents of the filter table on stderr.
Function: veDeviceToValuator
float veDeviceToValuator(VeDeviceEvent *e);Forces an event to be treated as a valuator. The actual effect of this function varies depending upon the input event. The result will be a valuator value. For triggers, the value is always 1.0. For switches and keyboards the value is either 0.0 (state == 0) or 1.0 (state == 1). For vectors, the first valuator in the vector is returned. For valuators, the value of the valuator is returned.This function is provided as a convenience and is meant to handle a reasonable number of common cases. More complex conversions should be done by hand or with the conversion filters.
e - The event to interpret.
Returns: A valuator value. If this is not a valuator type (valuator or vector) then the implicit range of the valuator is 0.0 - 1.0.
Function: veDeviceToSwitch
int veDeviceToSwitch(VeDeviceEvent *e);Forces an event to be treated as a switch. The actual effect of this function varies depending upon the input event. The result will be either 0 or 1. For triggers, the value is always 1. For switches and keyboards, the value of the event's state is returned. For valuators, a threshold at (min+max)/2 is set - values above the threshold are 1, values below the threshold are 0. If the valuator is unbounded then implicit ranges of 0.0 - 1.0 are used. Vectors are treated the same as a valuator, except that only the first value is used.This function is provided as a convenience and is meant to handle a reasonable number of common cases. More complex conversions should be done by hand or with the conversion filters.
e - The event to interpret.
Returns: A switch value.