Core Module
Navit is made up of several core modules for navigation, routing, maps, graphics, GUI, vehicle and more. Many core components are stubs which are extended with plugins: the core module provides the functionality that is shared between plugins, and the plugin provides the actual implementation. This allows us to support graphics on different platforms, different map formats, different vehicles (essentially GPS sources) and so on. Some modules are singletons (e.g. you can have only one GUI active at a time) while others allow multiple instances (e.g. you can have any number of maps).
It is rarely the case that you would actually need to develop a new core module for Navit from scratch. However, understanding the steps involved in creating one will help you understand how they work, should you ever need to add functionality a core module, or debug one.
Most of this information was gathered during development of the traffic module (which, as of December 2017, is still under development), with a lot of input taken from the proposed audio framework.
Source files
Most likely you will create a file named navit/mymodule.c to hold the
code for your module, replacing mymodule with the name of your module.
While not strictly a requirement, this is a convention followed in Navit
which makes the code easier to read. Wherever you encounter mymodule
as a name, or part of a name, in the following example, replace it with
the name you have chosen for your module.
Since you will likely be exporting function prototypes, structures and more from your module, you will also need a header file navit/mymodule.h to go with it. mymodule.c should contain a line like
#include "mymodule.h"
Next you need to tell the build chain about the new file. Edit navit/CMakeLists.txt and add mymodule.c to the list of source files near the top of the file.
Add an attribute type
Navit manages its core components in an object tree. Each core module is a Navit object class and thus has its own attribute type. When an object is instantiated, it is added to its parent as an attribute.
To add the attribute type for your module, edit navit/attr_def.h.
The file consists almost entirely of macros. You will see that it is sorted by attribute types. Look for the following line:
ATTR2(0x0008ffff,type_object_end)
Above that line, insert the following:
ATTR(mymodule)
Add basic definitions for your module
First you need to define a struct in which you will store all
information related to your module instance.
Next you need to provide methods for your module. Some methods are
optional (they can be NULL unless you need their functionality); for
others Navit provides default implementations that you can use if you
don’t need to do anything special. The only function you absolutely need
to write for yourself is the constructor function (though you can look
at other modules’ constructors to see what needs to go in there, and
copy over the relevant bits). Navit will call this function to create an
instance of your module.
Finally you need to tell Navit which methods to use for your module. Here is the code you need to add to mymodule.c:
#include "xmlconfig.h"
struct mymodule {
NAVIT_OBJECT
/* add members here as needed */
};
struct mymodule * mymodule_new(struct attr *parent, struct attr **attrs) {
struct mymodule *this_;
struct attr *attr;
/* an example of getting an attribute from the XML file */
attr = attr_search(attrs, NULL, attr_type);
if (!attr) {
dbg(lvl_error, "type missing\n");
return NULL;
}
dbg(lvl_debug, "type='%s'\n", attr->u.str);
/* call through to Navit’s constructor (mandatory) */
this_ = (struct mymodule *) navit_object_new(attrs, &mymodule_func, sizeof(struct mymodule));
/* unwind if there is a failure during initialization */
if (something_went_wrong) {
navit_object_destroy((struct navit_object *) this_);
return NULL;
}
dbg(lvl_debug,"return %p\n", this_);
/* reference the new object, else Navit will destroy it right after this function returns */
navit_object_ref((struct navit_object *) this_);
return this_;
}
struct object_func mymodule_func = {
attr_mymodule,
(object_func_new)mymodule_new,
(object_func_get_attr)navit_object_get_attr,
(object_func_iter_new)navit_object_attr_iter_new, /* or NULL */
(object_func_iter_destroy)navit_object_attr_iter_destroy, /* or NULL */
(object_func_set_attr)navit_object_set_attr, /* or NULL */
(object_func_add_attr)navit_object_add_attr, /* or NULL */
(object_func_remove_attr)navit_object_remove_attr, /* or NULL */
(object_func_init)NULL,
(object_func_destroy)navit_object_destroy, /* or NULL */
(object_func_dup)NULL,
(object_func_ref)navit_object_ref,
(object_func_unref)navit_object_unref,
};
You can add members of your choice to struct mymodule after the
NAVIT_OBJECT declaration.
For more information on what the struct object_func members do, look
at the documentation for struct object_func.
Next, edit navit/xmlconfig.c. First, we need to tell initStatic()
about the new XML element. Locate the function and edit the line near
the top:
elements=g_new0(struct element_func,44); //43 is a number of elements + ending NULL element
The number given here should be the number of elements (including your new one) plus 1, so you will likely need to increase it by 1.
Then add your element declarations after the ones that are already there:
elements[43].name="mymodule";
elements[43].parent="navit";
elements[43].func=NULL;
elements[43].type=attr_mymodule;
The index should be one higher than the highest existing index in the
list (and one lower than the number of elements you specified before).
The parent element is important: the configuration element for your
module in navit.xml must be a child element of the element you specify
here; and your module will be a child of that module in the object
hierarchy. In most cases your module will have navit as its parent,
but exceptions are possible. For example, maps have mapset as their
parent element. These instructions assume that your module will live
directly under navit.
Next, you need to tell Navit where to find the methods for your module.
This happens in the same file, in the object_func_lookup() function.
Look for the switch statement and add:
case attr_mymodule:
return &mymodule_func;
Since mymodule_func lives in a different source file, we need to
tell the toolchain to look for it. This happens in navit/xmlconfig.h.
Look for a line like the following:
extern struct object_func map_func, mapset_func, navit_func, osd_func, tracking_func, vehicle_func, maps_func, layout_func, roadprofile_func, vehicleprofile_func, layer_func, config_func, profile_option_func, script_func, log_func, speech_func, navigation_func, route_func;
Add mymodule_func to the list.
Tell the parent object about your module
As previously mentioned, the object instance for your module will get added to its parent as an attribute. This will only work if the parent knows what to do with the attribute type—whether that is the case depends on three things:
Are multiple instances of the module allowed?
Do the
add_attr,get_attrandremove_attrmethods for the parent point directly to the default implementations, or does the parent object implement its own method for one or more of them?Does the parent need to react in any way to the module being added, removed (if you need to support this) or queried?
If your module supports multiple instances, does not need any special
treatment by its parent and the parent uses the default add_attr
implementation, you’re good. Unfortunately, navit comes with its own
add_attr method which accepts only explicitly supported attributes.
Just read on.
Singleton modules
If your module allows only one instance, look at navit_add_attr in
navit/navit.c to see how Navit handles mapset, gui,
navigation or route children.
Parent overriding add_attr, get_attr or remove_attr
This is something you will need to take care of if the parent object of
your module is navit. In navit/navit.c, locate the
navit_add_attr() function. It looks something like this:
int
navit_add_attr(struct navit *this_, struct attr *attr)
{
int ret=1;
switch (attr->type) {
case attr_callback:
navit_add_callback(this_, attr->u.callback);
break;
/* some lines omitted... */
case attr_autozoom_max:
this_->autozoom_max = attr->u.num;
break;
case attr_layer:
case attr_script:
break;
default:
return 0;
}
if (ret)
this_->attrs=attr_generic_add_attr(this_->attrs, attr);
callback_list_call_attr_2(this_->attr_cbl, attr->type, this_, attr);
return ret;
}
If you look closely, you will see that the default behavior is to return
0 (indicating an error), unless the attribute type is explicitly handled
here. Some attributes are handled in a special way, but near the bottom
of the function there are two attribute types (attr_layer and
attr_script) which are simply passed through to
attr_generic_add_attr(). Add attr_mymodule here and you’re good.
This works in a similar way for get_attr and remove_attr. Also
look at the respective functions to see if they do what you expect.
Special handling by parent
Again, navit_add_attr() in navit/navit.c is a good example, as
several attribute types are handled in a special manner. Also look at
navit_get_attr() and navit_remove_attr().
If you are dealing with a parent which relies on the default implementation and you need some special handling, you need to write your own method for the parent. Test for the attribute type, have it do what it needs to do upon detecting your attribute type, then call through to the default implementation.
Initializing your module
You may need to set up a few things on startup, after the whole object
tree has been created. For instance, the GUI module needs to be
connected to the graphics module. The route and tracking modules need to
be told where to find the active mapset. These things happen in the
parent’s init function. Look at navit_init() in navit.c for
some examples.