These guidelines are informal descriptions of the sort of thinking that goes
into the design of Pidgin, libpurple, and family. These are not hard-and-fast
rules, but they are conscious decisions made by the Pidgin developers which will
be violated only after careful consideration. (Or by mistake, and corrected
While Pidgin is a multi-protocol IM client, the goal is to hide protocols from
the user as much as possible. Obviously users have to know about individual
protocols when they create or modify accounts, but in day-to-day communication
and usage, the intent is that users don't have to think about protocols at all.
The workflow in Pidgin is intended to be "I would like to chat with Sean about
wibbles", not "I would like to create an XMPP conversation with
The focus is on the goal, not the process. In reaching toward this focus, we
have chosen to paper over the differences between the various protocols and
features as much as possible (without crippling or needlessly complicating
things). This has led to decisions such as the removal of protocol icons from
the buddy list and the implementation of contact-aware chats and logs.
> Perfection is achieved, not when there is nothing more to add, but when there
is nothing left to take away.
> ---Antoine de Saint-Exupery
In general we try to keep the code and the user interface simple. Especially
when it comes to preferences in the UI. Pidgin should be as streamlined as
possible. It is an IM client, so messaging someone should be easy. See [this
) by Havoc Pennington for
an explanation of some ideas that we try to follow.
It's important for the code to be simple because this is an open source project
and developers come and go. You never know who's going to be looking at your
code next. If you write a function that's hard to follow then the next person
that comes along will end up rewriting it, and that's counterproductive.
In plain language, this means that the protocol-specific code goes in the
protocol plugin (*prpl*
), and that libpurple exists, and is cleanly separated
from the user interface. There are practical implications to this. While all
of our code depends on glib, only the Pidgin specific parts depend on GTK.
To implement, for example, file transfer; there are 3 steps. First, the
protocol(s) have to support it. By themselves, however, the protocols can do
nothing. So the "core," libpurple, has to support it also (the second step).
We do not want massive amounts of very similar code in libpurple, so the
implementation of file transfer at the libpurple level has to abstract away from
how individual protocols handle it, so as to be able to use the same calls from
all file transfer supporting protocols.
Last, but not least, before the user can actually send or receive a file, the UI
(Pidgin, Finch, or Adium) must support it. These interfaces know nothing about
the protocol, and have only limited contact with the core. This helps to
enforce the desire for uniformity explained above. It also makes it easier for
the only sort of duplication we encourage: many interfaces. The core
implementation cannot assume too much about what the UI will do, because the GTK
UI (Pidgin) might need to handle a file transfer somewhat differently than the
ncurses-based UI (Finch).
Patches that violate this layering will be rejected. In practice, this means
that there is more work involved to introduce a new class of functionality, say
file transfer, white-boarding, voice, or video. On the other hand, it means
less work to implement any given class of functionality for a new protocol or
Our source code is very much a 'live' document. It should reflect what is
currently needed, not what used to be needed or what might be needed in the
future. Old code should be removed if it isn't being used (of course, you can
only remove public functions and structures when the major version number
increases)---it'll always be in the source code repository if anyone needs it.
The code should contain documentation about what it does and why.
Pidgin and libpurple are single threaded. That means that the network code runs
in the same process as the user interface. Network code must be non-blocking,
otherwise the UI will be unresponsive. Code should be event-driven. Long
running tasks should be asynchronous. File descriptors that need to be watched
for changes should be added to the event loop.