File: ve_device.h

Contents

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.

Element Content

The core data in an event or an element is called element content.

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 is VE_ELEM_TRIGGER then there is no effectively no content. For consistency this structure is defined for a trigger, but currently it contains no fields beyond the ones in VeDeviceEContent.
typedef struct ve_device_e_trigger {
  VeDeviceEType type;
} VeDeviceE_Trigger;

Structure: VeDeviceE_Switch

If the type of content is VE_ELEM_SWITCH then 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 is VE_ELEM_VALUATOR then 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 is VE_ELEM_VECTOR then 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 is VE_ELEM_KEYBOARD then 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.h header 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_VECTOR then 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.

Elements and Device Models

The first place that element content is used is in elements and device models. A device model is an abstract representation of a device which shows both its structure and its current state.

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_VECTOR then 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:
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: "{}".
For example:
elem foobar valuator -5.0 5.0
would 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 NULL pointer 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. See ve_util.h for 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 for
veDeviceAddElem(model,veDeviceParseElem(spec))
with some added error checking.

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. NULL otherwise.

Device Manifest

Devices are put together from a number of pieces. First, a device description describes that specifics of a particular device - name, type, driver, settings, etc. These descriptions are usually read in from a manifest. A manifest lists possible devices in the system. A device needs to be used before it is accessible to the system. The manifest is generally fixed for a particular computer system. Individual applications then specify which devices they actually use. There is general support in the VE library for loading a device usage file at run-time so that the set of devices to use and their mappings can be easily loaded at run-time.

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, NULL otherwise.

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, NULL otherwise.

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.h for 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 NULL if 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, call veClearDeviceManifest() before calling this function.

The following entries can be found in a manifest file:

Blank lines and lines beginning with '#' are ignored.

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 NULL if there is currently no device manifest.

Devices and Drivers

Support for devices is provided through device drivers. Each driver is responsible for providing support for a particular device type. The VE library keeps an internal table mapping known device types to drivers. When a device is created in the system, the VE library uses the driver to instantiate the device, creating both a link to the driver but a private structure for the driver containing the information specific to that instance of the device. A device description (see the section on the manifest above) is required to instantiate a device.

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 NULL on 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 NULL if there is none.

idata - The private (driver-specific) structure for this instance, or NULL if there is none.

desc - The description from which to initialize the option set, or NULL if no description is provided.

override - A set of options that override those in the description or NULL if there are no override options.

Returns: A pointer to a newly created instance structure if successful, or NULL if an error occurred.

Function: veDeviceInstOption

char *veDeviceInstOption(VeDeviceInstance *i, char *name);
Retrieves an option from an instance. Device driver writers should create an instance with veDeviceInstanceInit() and retrieve options with veDeviceInstOption() rather than veDeviceDescOption().

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, NULL otherwise.

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, NULL if 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. See veDeviceUse() 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 using veDeviceFind() 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 NULL then a purely virtual device is created (i.e. without a model).

Returns: A pointer to the newly-created VeDevice object or NULL if 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 a veDeviceUseByDesc() or veDeviceUse() 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. NULL is 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 as veDeviceUseByDesc() 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. NULL is 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, NULL if the device is not in use.

Events

Real devices generate events. Events combine element data with information about the time the event occured and from which device and element the event is believed to have originated.

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_KEYBOARD then 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_TRIGGER then 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_SWITCH then 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_VALUATOR then 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_VECTOR then 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_VECTOR then this argument is the size of the vector.

Returns: A pointer to the newly created object, or NULL if 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 call veDeviceEventInit(type,vsize,NULL,NULL) is not equivalent to veDeviceEventCreate(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_VECTOR then this argument is the size of the vector.

device - The name of the device from which this event originates, or NULL to not initialize the device field.

elem - The name of the element from which this event originates, or NULL to not initialize the event field.

Returns: A pointer to the newly created object, or NULL if 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 as veDeviceEventInit().

device - The name of the device to use, or the NULL to leave the device field of the event unspecified.

Returns: A pointer to the newly created object, or NULL if 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 uses veDeviceApplyEventToModel() 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.

Callbacks

A device sets up any number of callbacks to process events. When an event is received, it is filtered and then passed to the first callback that matches its device and element specifications.

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.index
Any 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 NULL if 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.

Filters

Incoming events can be passed through any number of filters. Filters can generally modify events, including changing data, renaming events, discarding events or generating new events.

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:

VE_FILT_CONTINUE
Continue processing the event with the next applicable filter in the table. If no applicable filters remain, pass the event to the appropriate callbacks, if any.
VE_FILT_RESTART
Continue processing the event, but start at the beginning of the table rather than the current position. Beware that it is possible to build infinite loops in the filter table using the restart result.
VE_FILT_DISCARD
Stop all further filter processing of the event and discard it without passing it to callbacks.
VE_FILT_DELIVER
Stop all further filter processing of the event and immediately pass it to any applicable callbacks.
VE_FILT_ERROR
Indicates that the filter encountered an error in processing the event. The event is discarded and an error reported through VE's error module.
VE_FILT_EXIT
Indicates that the program as a whole should terminate. This is provided for convenience so that a device event can be easily made to exit the program.

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 NULL then 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 NULL if 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 NULL then 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.button
has a single positional argument, and
to_valuator expr=1.0*x min=0.0 max=1.0
has three named arguments. Named arguments and positional arguments can be mixed, e.g.
junk a b v=199 c d

There 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, NULL on error.

Filter Table

How to apply filters to events is described in the system's filter table. The filter table is a sequence of filter chains. Each chain has a device specification describing what events it applies to. The chain itself is made up of a sequence of linked filter instances. A filter chain is applied if its device specification is matched. Unless a filter halts processing, each filter in the chain is applied in succession.

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 NULL if 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_HEAD then the entry is inserted at the beginning of the table. If where is VE_FTABLE_TAIL then 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:
veDeviceFTableAdd(veDeviceParseFTableEntry(desc),where)
with some extra error checking.

desc - The description of the filter table entry as would be acceptable to veDeviceParseFTableEntry().

where - If where is VE_FTABLE_HEAD then the entry is inserted at the beginning of the table. If where is VE_FTABLE_TAIL then 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 NULL then 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, NULL otherwise.

Event Queue

Events are conceptually organized into a queue. All events are processed in the order that they arrive. Events are not necessarily queued - if no events are being processed when an event arrives from a device, it is processed immediately. Events are only queued when other processing is going on that prevents the event from being processed immediately. Events that are generated due to processing another event are also queued (e.g. from a filter) are added to queue but are typically added at the head of the queue rather than the tail since they are assumed to have occured at the same time as the original event.

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_HEAD then the event is added to the head of the queue. If this is VE_QUEUE_TAIL then 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_HEAD then the event is retrieved from the head of the queue. If this is VE_QUEUE_TAIL then 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. NULL is 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_ERROR status).

Function: veDeviceInsertEvent

int veDeviceInsertEvent(VeDeviceEvent *e);
Similar to veDeviceProcessEvent() 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_ERROR status). 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 until veDeviceUnblockEvents() is called.

disp - The disposition towards events. The following constants are defined and recognized:

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 by veInit().

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.

Index