Inkscape Developer's Manual
For those of you just joining us, or who have been with us but are just now getting the itch to work on Inkscape, I thought I'd give some tips for how to get started working in the codebase based on our own experiences.
One of the first things most people wonder is "what should I work on". As you may have already noticed, we generally don't "assign" projects. We figure there's plenty more work to do than people to do it, so you may as well work on something that you're either interested in or that adds something of benefit to you; that's extra motivation to get your own itches scratched.
If you're really stumped though, we keep a detailed roadmap in wiki that you're welcome to browse through to look for ideas of things to work on. Tasks that do not have names beside them are open for anyone to take; if you want to take ownership of a task, just put your name beside it. Feel free to add or reword tasks as needed, although try not to load up the current milestone with tasks that aren't critical for the release. Feel free to work on stuff that is several milestones down the road; there's rarely any problem with getting stuff done sooner than planned. ;-)
We have a process for gaining CVS commit access. The reason is that while it is important that we keep access to the codebase open, we don't want to be crazy and leave it wide open to any random passer-by. The process is that we require that the person make two contributions (patches, documentation, web collateral, etc.) and then make a request to get account access.
In general you won't need CVS commit access in order to start doing development, because you can work from an anonymous checkout and create patches. If you've not done this before, you'll need to learn this skill first (basically see docs for `cvs diff`).
When you first start hacking on Inkscape code, I wouldn't recommend taking an objective of implementing a specific feature, because you will need some time to familiarize yourself with the codebase, and because you won't really know what features are going to be straightforward to implement and which will be highly challenging. Of course, if you have the time and love adventures, this might be fun way to go.
There are four approaches that I've seen people effectively use in getting into the codebase:
- Write code documentation. Some people who don't mind adding comments to code or writing docs find it useful to just go through code they're interested in working on and writing up what it does. The codebase is in dire need of better docs, so this approach pays dividends well into the future.
- Fix bugs. Tracing down the cause of reported bugs is an effective way to gain understanding of the codebase in small chunks. Many common bugs can be traced down and fixed in a matter of hours, and often will identify some bit of code in need of refactoring or extension. Note that some of our older bugs are in the system because they're hard to fix, so you'll want to work on the more recent ones.
- Chip in on a group effort. Occasionally we identify a major refactoring effort (such as when we converted from C to C++), that we encourage lots of people to help on, in the philosophy that many hands makes short work. This work tends to be pretty rote so is not hard for new folks to get involved with; just takes time. We generally have one of these kinds of efforts per release. It usually isn't glamorous work, but in aggregate moves the codebase forward in a major way.
- Subsystem/module work. Some people want to get their hands in the details quick, so take the approach of developing new code separate from the codebase, to be integrated in later. This generally tends to take a larger time commitment than the other approaches, but can an effective approach in some circumstances. We have a CVS module called 'experimental' that you're welcome to house your work until it's ready for prime time.
Beyond that, you're going to find the documentation for the Inkscape code is pretty scarce. We've worked on bits and pieces but unfortunately the vast majority of the code is undocumented. On the plus side, often you can implement the stuff you care about after learning only a limited portion of the codebase.
I think you'd find Inkscape an enjoyable Open Source project to work on. There's a huge range of interesting and useful skills that can be learned from it, plus the developers are great guys to participate with. The project itself runs smoothly and puts a premium on keeping things friendly and low-stress, so heated arguments are rare. The users have been great to work with and very appreciative of even small new features and fixes. Plus, since Inkscape is so visual in nature, it's very cool to see how your little changes make noticeable improvements to the app overall.
We strongly recommend that everyone read this site thoroughly and bookmark it for reference:
It's actually more in-depth than the name FAQ warrants. Many experienced C++ programmers would benefit from it.
Please make sure any user-visible strings are localizable. This requires wrapping them with "_(" and ")", like so:
For more complex things, please check the gettext/localization documentation.
Standards Compliance - Extension Namespaces
- Only elements and attributes from our extension namespaces that do not affect rendering may be saved in SVG documents.
- Generally, this means that extension elements and attributes should only be used to provide UI hints.
- Extension elements and attributes should only be used where an existing facility provided by XML or SVG is not sufficient.
Here's a readers' digest summary of how Inkscape accelerators work:
A global mapping between key combinations and integer verb IDs (sp_verb_t) is maintained in shortcuts.cpp; these are registered using sp_shortcut_set().
Given an sp_verb_t and an SPView, you can get an SPAction which represents that action in that view. These mappings are currently hard-coded in verbs.cpp.
SPActions derive from NRActiveObject, which putatively provides a "lightweight" method of doing callbacks, versus GObject signals. I don't completely understand how it works.
SPActions also contain the label, image, etc, used for buttons and menuitems.
sp_shortcut_invoke() looks up the SPAction for a keypress and SPView and invokes it automatically. SPEventContexts call it for keypresses that they do not handle themselves.
As you know, many automatic garbage collectors (like libgc) only free and recycle memory periodically. This means you may have some extra slush that could be freed, but hasn't yet.
There are other forces at work, though...
Pretty much all allocators, whether automatic or not, whether the system malloc() or some custom allocator like libgc's, work the same way: they request large blocks of memory from the operating system, then divvy those blocks into smaller ones internally to satisfy application allocation requests.
When an application frees memory, that memory is usually recycled internally rather than returned to the OS immediately. The reason for this is that the large memory blocks acquired from the OS must be completely unused before they can actually be freed.
Let's say for example that an allocator acquires 16 8MB blocks from the OS in response to 32768 4k application allocations...
In a worst-case scenario, it's possible that the application could free 32752 of those 4k blocks but the remaining 16 4k just happen to be distributed across the 16 8MB blocks requested from the OS.
If that happens, from the application's point of view it may only have 64k allocated, but as far as the OS is concerned, it's still using 128MB!
Note that this applies to nearly all allocators in common use.
While it's unusual for things to get quite that bad, memory fragmentation is common enough that many popular allocators (for example Perl's) simply don't bother trying to return memory to the OS at all (the memory will still get forcibly reclaimed by the OS when the process exits).
[ FWIW, libgc's allocator is one of the ones that _does_ make an effort to release memory to the OS, but it is limited by fragmentation like any other ]
Also note that for various reasons, the statistics you get from the OS aren't going to directly reflect the amount of heap-allocated memory. Be careful drawing conclusions from only looking at e.g. the output of top(1)...
(the worst thing is that due to the modern practice of overcommitting memory, the OS may literally lie to an application about the amount of memory it is being given, hoping the application won't really try to use it all)
The best approach to evaluating memory usage is if you can ask the allocator for information on memory usage directly, as that matches the world from the point of view of the application.
leftover gradients/markers/patterns will get automatically cleaned up when the objects that use them are deleted.
- this only applies to such objects created with a build of Inkscape which post-dates this commit (June 7)
- not all automatically-created objects will necessarily be collected; the code that creates them needs to be updated to set the correct collection policy
- paint objects won't get collected until another editing operation takes place, since NRArenaShape currently holds onto an SPStyle for too long
Assuming its collection policy permits it, an object will be collected if neither it nor its descendants have any outstanding inter-document URI references (nonzero SPObject::hrefcount).
There are two "policies" for collecting orphans:
- "with-parent" - the object will only be collected if one of its ancestors is collected
- "always" - the object is always collected if unused
(a third policy might be "never", which would necessarily also prevent that object's ancestors from ever being collected; I do not plan on implementing it)
The policy in effect is determined by the inkscape:collect attribute.
Be careful with the "always" policy; it really only makes sense for "private" objects that are indirectly created behind the scenes (e.g. by selecting a fill or marker option in the GUI).
SPDocument manages a queue of objects to collect; SPObject handles the machinery for actually queueing them when their hrefcount falls (based on policy), and performing the actual collection (delete). SPDocument::collectObjects() performs a collection pass; it's currently only called from sp_document_maybe_done().
Inkscape Experimental CVS
The 'experimental' module in Inkscape CVS is provided as a kind of "scratchpad" for working up new ideas that aren't quite ready for folding into the main codebase. This includes architectural sketches, examples, experimental patchsets, tools & utilities, or whatever else strikes the developer's fancy.
Please create a subdirectory within experimental/ for your work. You're welcome to either post the stuff at the top level or create a subdirectory for yourself. Things linked in at the top level should be considered fair game for other developers to collaborate on; items posted under a developer's username should be considered ask-first. Same sort of idea as wiki.
One of the principles behind this module is the idea of a shared working space. Other developers working in experimental can fairly easily see what others are working on in the tree, and perhaps borrow or contribute ideas back and forth. Since it is by definition not 'production' code, the work may be incomplete or in a non-compileable state, and thats O-K.
When an experiment has matured to the point of being actually useful, please move it out of the experimental module to someplace more appropriate. Or alternatively if the experimental work has become obsolete or irrelevant, please remove it so we can avoid having the experimental tree get too bulky.
Distribution / Packaging Files
Files related to generation of distribution packages should go under inkscape/packaging, as follows:
inkscape/packaging/ common/ debian/ fedora/ fink/ mandrake/ suse/
A variety of items are installed in addition to the program itself, and placed into a 'share' directory structured as follows:
AUTHORS NEWS clipart/ examples/ extensions/ fonts/ gradients/ icons/ keyboards/ markers/ palettes/ patterns/ screens/ about.svg templates/ tutorials/
In the CVS codebase, all of these are placed in inkscape/share/ (except AUTHORS and NEWS which will be copied to share during installation. The idea is that in theory, this entire tree structure can be copied into place on the user's machine.
However, we need to provide the user some level of control over the installation. They may wish to exclude some items, or may wish to augment the default install with some items external to the Inkscape package. For example, they may wish to incorporate external clipart collections. One approach would be to install symlinks in the given component directory to the external collection. For example, if the flags package were to install into /usr/share/flags-svg/, we'd just symlink there.