A plugin can serve as an I/O interface. Note the difference between plain import/export and a full I/O - do not implement an I/O for a simple import or export task but do not implement a pair of import/export plugins for an I/O task either.
A plugin can register multiple I/O interfaces. This is typical for formats with multiple versions, or for format families with alternative sub-formats for the same purpose. But the most common setup is that an io plugin registers one I/O interface.
Each I/O interface has a name and priorities. The name is displayed to the user and priorities determine in what order different options are presented - this helps ordering common file formats first, exotic options last.
Each I/O interface also has hooks for testing whether it will be able to load an input file, before doing the real parse. When a file needs to be loaded, the plugins that will attempt to load are not selected by the file name, but by file content.
For more details on the I/O interface fields, callback functions and mechanism, refer to src/plug_io.h.
Declare a static variable of pcb_plug_io_t and fill it in from the plugin init callback:
static pcb_plug_io_t io_pluginname; ... int pplg_init_io_pluginname(void) { PCB_API_CHK_VER; memset(&io_pluginname, 0, sizeof(io_pluginname)); /* these are mandatory: */ io_pluginname.default_fmt = "shortname"; io_pluginname.description = "longname"; io_pluginname.save_preference_prio = 89; io_pluginname.default_extension = ".foo"; io_pluginname.fp_extension = ".bar"; io_pluginname.mime_type = "application/x-pcb-layout"; io_pluginname.fmt_support_prio = io_pluginname_fmt; io_pluginname.test_parse = io_pluginname_test_parse; /* these are optional (leave NULL if not supported): */ io_pluginname.plugin_data = &ctx; io_pluginname.parse_pcb = io_pluginname_parse_pcb; io_pluginname.parse_footprint = io_pluginname_parse_footprint; io_pluginname.parse_font = io_pluginname_parse_font; io_pluginname.write_buffer = io_pluginname_write_buffer; io_pluginname.write_footprint = io_pluginname_write_footprint; io_pluginname.write_pcb = io_pluginname_write_pcb; /* the actual registration */ PCB_HOOK_REGISTER(pcb_plug_io_t, pcb_plug_io_chain, &io_pluginname);
The memset is required in case the core struct is extended with new fields. The convention is that NULL or 0 is the default/unused value. With the memset extending the core struct won't force us to update all plugins.
On plugin uninit any I/O interface registered by the plugin must be removed. This is done using the PCB_HOOK_UNREGISTER() macro:
PCB_HOOK_UNREGISTER(pcb_plug_io_t, pcb_plug_io_chain, &io_pluginname);