PURPOSE
LibGIIGIC is intended to help the game developer with a flexible input mapping system which enables the user to easily adjust the bindings between game actions and the input device movements.NO FEAR
Please do not get confused by the tons of API calls. Most are only useful if you are developing new configmanagers or applications that have very special needs, like reconfiguring their controlset on the fly.A "normal" game will usually only use a very small subset of the LibGIIGIC API, depending if it wants to just use a given GIC configuration, or if it also wants to implement a configuration manager.
Actually it is a good idea, to at least allow the user to run his own config manager if he wants to.
TERMINOLOGY
LibGIIGIC has its own terminology that describes how an application and the actions that can be triggered within it relate. This can be confusing at first, so please make sure you read this section carefully and refer to it, if unsure.From LibGIIGIC's point of view, an application is represented as a "Head". This term stems from the fact, that an application might serve multiple displays (heads) with different configurations. LibGGI will usually have its own inputs for each head, then.
Within each head, applications will usually have different needs for input mapping, depending on the current state of the application. In LibGIIGIC this is called a "Context".
Each context (like "in game", "main menu", ...) will usually have several aspects that can be controlled, for example movement, weapon choice, etc. This is what LibGIIGIC calls a "Control".
Most Controls have several subunits. For example movement may contain "forward", "backward", "left" and "right". LibGIIGIC calls these "Features".
Most of the time, one feature can be controlled by a single keystroke, mouse-click, joystick movement, etc. However sometimes it makes sense to have multiple bindings for the same feature.
Any such user input that is associated to a feature is called a "Recognizer".
This hierarchy of types that builds the skeleton of LibGIIGIC.
It can be represented as a file. Look at programs/demos/configmanager.gic for an example. Such a ".gic-file" contains all the configuration info for a given head.
Of course it is not enough to just recognize, that a feature has been activated by some input. One usually wants to act on that.
LibGIIGIC allows to bind callback functions to features that get called when a Recognizer triggers. These are called "Actions".
SIMPLE EXAMPLE
Now let's have a look at a very simple sample program - demo.cWhen working with LibGIIGIC, you first need to initialize the library. Do this using gicInit(). When you don't need LibGIIGIC anymore, close it down using gicExit(). Don't be afraid to use that in higher level libraries. LibGIIGIC is aware of being initialized and exited multiple times, and will handle it correctly, as long as the calls are nested properly.
These calls only initialize the lib. To interact with it, you need to allocate a gic_handle_t using gicOpen(NULL). The parameter is reserved for future expansion. When you are done with it, free the allocated handle using gicClose(handle).
If you have an existing configuration file, you just read it:
config = fopen("demo.gic", "r"); head = gicHeadRead(handle, config); fclose(config);
Simple - right?
You can of course stuff your other data there, too. gicHeadRead(3) is aware of where to stop, so you can store your data before and after the LibGIIGIC data to your liking.
If you are interested in rehearsing the terminoloy chapter, you may want to look at the demo.gic file yourself. It is human readable and you will find the hierarchy of objects in there.
Now what you got back is an object "head" which is of type gic_head(3).
However, you will usually work with contexts, as the application will normally enter different states associated with gic_context(3)'s.
You can extract the contexts from the head like this:
menu = gicHeadLookupContext(handle, head, "Menu context");
Now we have one point that needs to be cleaned up: the file also stores actions, which are basically callbacks. However how does one store function pointers in a config file in a way that does not break when moving the config file between platforms etc. ?
You don't. The next step is to reestablish the "action mapping" - a connection between a symbolic name of the action, and the actual callback function. This mapping is stored in a variable of type gic_actionlist.
A sample list would be:
gic_actionlist actionmapping[]= { {NULL,"mynextaction", my_action, NULL}, {NULL,"myprevaction", my_action2, (void *)0x12345678}, {NULL,NULL,NULL,NULL} };
The first element of each gic_actionlist-entry here is reserved for a chaining pointer and always NULL.
The second element is the name of the action as it appears in the .gic-file. The third is the callback function, and the fourth a void pointer to some private data which will be given to the callback.
The fourth entry allows to reuse the same callback function for multiple purposes, which makes a lot of sense, as many callback functions are usually very similar.
You can now re-map the actions using this call:
gicHeadMapActions(handle,head,actionmapping);
This will cause LibGIIGIC to iterate through the head and its child objects and find any actions attached. If the action names match a name in the actionmapping list, the respective callbacks are set up.
From then on, LibGIIGIC is ready to receive events. You just receive a gii_event from LibGII (or LibGGI which uses LibGII for input), or build one yourself, if you insist not to use LibGII.
You can then feed the event to LibGIIGIC using:
gicContextHandleEvent(handle,menu, &event);
Note, that it does not make sense to send an event to a head, as the event might have different meanings depending on context. So you just feed it to the right context.
Now if an event matches the description in the .gic file, the requested action callbacks will fire. We will discuss them in greater detail in the next section.
When we are done with LibGIIGIC, we should close down all handles we opened using gicClose(handle) and finally call gicExit().
CALLBACK FUNCTIONS (ACTIONS)
If a recognizer triggers, it will activate the feature it is attached to, which will in turn call the actions bound to it.Action functions have the following prototype:
void my_action(gic_handle_t hand, gic_actionlist *action, gic_feature *feature, gic_state newstate,gic_flag flag, int recnum);
When it gets called, it is given a lot of data that allows to derive why it was called.
First of all it gets the gic_handle_t that triggered the action. This is useful, if you want to call LibGIIGIC functions within the callback.
Then you get the gic_actionlist of the action that caused the callback to be actived. This datatype was already covered in the previous section. It's most common uses are differentiating calls to a common callback that serves multiple purposes. To achieve this, the most interesting entries are action->name and action->privdata, which will commonly be used to find out which action triggered and to point to some common data structure that will be modified by the actions.
The gic_feature(3) points to the feature that this action is bound to and can as well be used to differentiate when binding a single action function to multiple features.
When a feature gets activated, it can have an "extent to which it is activated". This is used by features that don't only have on/off characteristics, but rather a linear notion of "how active they are". An example for this is e.g. a control for turning. Simple input devices like keyboards can only say turn left/don't turn left, while mice or joysticks can say how fast or far to turn.
gic_state carries this information. The macros GIC_STATE_MIN, GIC_STATE_MAX, GIC_STATE_MIDDLE and GIC_NOACTION can be used to find out how strong the respective measure of activation was.
gic_flag can carry additional information about the action. If flags are set, that are within GIC_FLAG_MUSTKNOWMASK, you are expected to handle them. Set flags outside that bitmask are optional and can be ignored for application development.
Up to now, only one such flag is defined: GIC_FLAG_PULSE. If set, it indicates, that the events triggering the action is of a "pulsed" type, i.e. it is expected to be released immediately after it was received, so you should handle it similar to the case of an event like the one you received, immediately followed by one of the same kind with gic_state containing GIC_STATE_MIN.
The integer recnum gives the number of the recognizer that caused this event to trigger. This can be used when implementing "adding policies", like if turning with joystick and mouse at the same time will add up, or will just use the highest value, or whatever.
USING THE LAZY ACTION HELPERS
Many simple programs will prefer not to use their own action callbacks, but rather just use this predefined callback system.For this to work, you have to bind all relevant actions you do not want to handle yourself to gicActionLazyAction(3).
The privdata field must point to a gicActionLazyData(3) variable.
When using the Lazy action helpers, you usually do something like this:
ggiEventRead(vis,&event,emAll); /* get an event. */ /* Let LibGIC evaluate it for the menu context */ gicContextHandleEvent(hand, menucon, &event);
Now the gicActionLazyAction(3) callbacks are run, and you can check how much the individual gicActionLazyData(3) elements are active.
For this, you do: gicActionLazyGetstate(&lazydata) which will return a gic_state you can evaluate. (The gicActionLazyAction(3) callback implement a "max activation is returned" policy with respect to multiple active recognizers).
This will also handle the case of pulsed inputs, which will be returned and then deleted.
If you are unsure about the state of the variables (e.g. at startup or after retraining the mappings), you can reset the variables using gicActionLazyReset(3).