NAME
libgiigic-usage - LibGIIGIC Overview
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.c
When 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).
TRAINING
TODO
CONFLICTS
TODO
SEE ALSO
libgiigic(7)