[GSoC] GObjectification progress as of July 22, 2013

Ankit Vani a at nevitus.org
Mon Jul 22 17:42:58 EDT 2013


Hi everyone!

I haven't posted an update in three weeks, and I apologize for so much delay.
So without wasting more time, here it is. :)

This mail describes work done by me since July 1, and does not include every
change. Take a look at the files in my repo for the entire picture.
My repo: http://hg.pidgin.im/soc/2013/ankitkv/gobjectification/
Branch:  soc.2013.gobjectification


Buddy List
----------

Object hierarchy:

 GObject
  +----PurpleBuddyList
  +----PurpleBListNode [abstract]
        +----PurpleBuddy
        +----PurpleChat
        +----PurpleCountingNode [abstract]
              +----PurpleContact
              +----PurpleGroup

The buddy list is one of the main components of libpurple. However, things were
easier since it already followed a hierarchy among nodes.

What's new in the hierarchy is the abstract type PurpleCountingNode, which
inherits PurpleBListNode. The idea for such a type was initially given by Gary
(grim). The purpose of PurpleCountingNode is to keep track of the counts of the
children of a node, to avoid code duplication for that purpose in both
PurpleContact and PurpleGroup. PurpleCountingNode keeps track of:
1. total_size:   total number of children of the node (eg. the number of buddies
                under a contact)
2. current_size: the number of children of the node corresponding to online
                accounts
3. online_count: the number of children of the node that are currently online

For each of these counts, the API provides these functions:
- purple_counting_node_set_*    to set counts
- purple_counting_node_change_* to change counts by a given value
- purple_counting_node_get_*    to get counts

The instance structs for PurpleBListNode has the public pointers prev, next,
child and parent, to facilitate list/node operations. All other members for
PurpleBListNode and all the derived types are private, and can be accessed via
the API only.

One thing you would notice here is that I have renamed PurpleBlistNode to
PurpleBListNode, to adhere to the naming convention I'm following, and for
clarity. For uniformity, PurpleBlistUiOps has been renamed to PurpleBListUiOps.
If someone has a valid argument against these renames, please let me know and
they can easily be renamed back.

PurpleBlistNodeFlags has been removed. For a long time, only one flag has
existed (ie. PURPLE_BLIST_NODE_FLAG_NO_SAVE). I have converted this to a
property of PurpleBListNode, and can be used with
purple_blist_node_set_dont_save(), purple_blist_node_get_dont_save().

Some old functions have been removed due to the introduction of
PurpleCountingNode.
purple_blist_update_node_icon() has also been deleted, due to it's misleading
name and the fact that the only thing it did was call ui_ops->update(). So just
use that directly.

purple_blist_ is the buddy list subsystem API and purple_buddy_list_ are
functions for the PurpleBuddyList object. Although as of right now, the only
purple_buddy_list_ function is purple_buddy_list_get_type(), that returns the
GType for the object. However, the function purple_get_blist() has been renamed
appropriately to purple_blist_get_buddy_list().

Some functions in the API have been renamed to/replaced by more
appropriate/simpler functions. Some examples:
- purple_buddy_get_local_buddy_alias() to  purple_buddy_get_local_alias()
- purple_blist_update_buddy_status()   to  purple_buddy_update_status()
- purple_blist_rename_buddy()          to  purple_buddy_set_name()
- purple_blist_alias_contact()         to  purple_contact_set_alias()
- purple_blist_alias_chat()            to  purple_chat_set_alias()
- purple_blist_alias_buddy()           to  purple_buddy_set_local_alias()
- purple_blist_server_alias_buddy()    to  purple_buddy_set_server_alias()
- purple_blist_rename_group()          to  purple_group_set_name()
- purple_blist_merge_contact()         to  purple_contact_merge()
- purple_find_buddy_in_group()         to  purple_blist_find_buddy_in_group()
- purple_find_buddy()                  to  purple_blist_find_buddy()
- purple_find_buddies()                to  purple_blist_find_buddies()
- purple_find_group()                  to  purple_blist_find_group()

blist.[ch] has been split into three pairs of files:
1. buddylist.[ch]:      The PurpleBuddyList object and the buddy list API
2. blistnode.[ch]:      PurpleBListNode and PurpleCountingNode abstract types
                        and their API
3. blistnodetypes.[ch]: PurpleBuddy, PurpleChat, PurpleContact and PurpleGroup
                        with their APIs


Connections
-----------

Object hierarchy:

 GObject
  +----PurpleConnection

 GBoxed
  +----PurpleConnectionErrorInfo

The PurpleConnection object doesn't have a lot of changes yet. I plan to make
changes to the way connections work when I start working on protocols. But as of
now, PurpleConnection is a GObject.

GFlags for PurpleConnectionFlags, and GEnum for PurpleConnectionState and
PurpleConnectionError are automatically generated in enums.[ch] using
glib-mkenums, as described in my previous mail.

For clarity, the prefix of PurpleConnectionFlags has been changed from
PURPLE_CONNECTION_ to PURPLE_CONNECTION_FLAG_ (eg. PURPLE_CONNECTION_FLAG_HTML)
Similarly, the prefix of PurpleConnectionState has been changed from from
PURPLE_ TO PURPLE_CONNECTION_ (eg. PURPLE_CONNECTION_CONNECTED)

PurpleConnection's private member buddy_chats has been renamed to a more
appropriate active_chats. The function purple_connection_get_active_chats() has
been added to get the active chats on a connection.

When refactoring the codebase to use the GObject-based connection API, I had run
into some issues with the jabber protocol.
I would like to discuss these issues to make sure they are handled properly.
I would appreciate insight and suggestions.

1. In libpurple/protocols/jabber/jabber.c
   Line: http://hg.pidgin.im/pidgin/main/file/3b8ce83bac96/libpurple/protocols/jabber/jabber.c#l1378
   Here, the member wants_to_die of a connection is set directly. I believe this
   setting this directly should be avoided. And because the member wants_to_die
   is now private due to GObjectification, it cannot be accessed directly even
   if we wanted to. At first, I did not want to add a function to do something
   like this as it sounded.. not right.

   However, for the time being, I added the function
   purple_connection_disable_reconnection(), which does what is expected from
   it's name. It is intended for protocols to handle user registration due to
   queries, which as of now is only done by jabber.

2. In libpurple/protocols/jabber/parser.c
   Line: http://hg.pidgin.im/pidgin/main/file/3b8ce83bac96/libpurple/protocols/jabber/parser.c#l311
   Here, the member disconnect_timeout of a connection is accessed directly. I
   don't think this member should be accessed directly at all, under any
   circumstance. Also, the check does seem a little unnecessary, but I haven't
   spent enough time with protocols to know for sure. No other protocol does
   this.

   For now, I have disabled that bit with #if 0. Please let me know if this
   check if really necessary, and if so, I would appreciate some help.


Statuses
--------

Object hierarchy:

 GObject
  +----PurpleStatus

 GBoxed
  +----PurpleStatusType
  +----PurpleStatusAttribute
  +----PurpleMood

Not a lot of API changes here, except for using GValues instead of PurpleValues
for attributes. The removal of PurpleValue is convered in more detail in its
own section below.

purple_status_get_type() has been renamed to purple_status_get_status_type()
which returns a PurpleStatusType, to avoid a name clash with
purple_status_get_type() which returns the GType for PurpleStatus.
purple_entity_get_type() for any GObject or GBoxed returns that particular
entity's GType.

PurpleStatusAttr has been renamed to PurpleStatusAttribute for simplicity.
Thus, purple_status_attr_* functions are now purple_status_attribute_*.


Presence
--------

Object hierarchy:

 GObject
  +----PurplePresence [abstract]
        +----PurpleAccountPresence
        +----PurpleBuddyPresence

PurplePresence has been moved from status.[ch] to a new pair of files
presence.[ch]. Also, Presence.xs has been added to
libpurple/plugins/perl/common.

The enum PurplePresenceContext has been removed, since this is no longer
necessary, by using subclasses.
The union in PurplePresence to store either an account, conversation or a buddy
has been removed. Instead, subclasses can be used to store the account or buddy
data.

There is no PurpleConversationPresence, as the conversation context was never
really used. If this is to be used later, a plugin should be able to subclass
PurplePresence.

purple_presence_new(), purple_presence_new_for_account() and
purple_presence_new_for_buddy() have been removed in favor of
purple_account_presence_new() and purple_buddy_presence_new().

For PurpleBuddyPresence, the members account and name have been removed, as they
were rarely used, and can be easily retrieved by getting the buddy and calling
purple_buddy_get_account() or purple_buddy_get_name() on it.

Some functions have been renamed to indicate the type that they operate on:
- purple_presence_get_account() to  purple_account_presence_get_account()
- purple_presence_get_buddy()   to  purple_buddy_presence_get_buddy()
- purple_presence_compare()     to  purple_buddy_presence_compare()

A pure virtual method update_idle has been added to PurplePresenceClass that
subclasses must implement, to update the idle state of the presence to logs and
UI, depending on the type of the presence.


Removal of PurpleValue and PurpleType
-------------------------------------

PurpleValue and PurpleType have been removed (along with the files value.h and
value.c). PurpleValue was just a more complicated GValue and PurpleType was a
very limited GType. Thus, I decided to just use GValue and GType instead. :)

In most cases where PurpleValue was used, using GType was enough instead of
having to use GValue. Apparently, we used to create PurpleValue that didn't
actually hold any value in them just to indicate types. For example, to register
a signal, we had:

purple_signal_register(handle, "account-alias-changed",
purple_marshal_VOID__POINTER_POINTER, NULL, 2,
purple_value_new(PURPLE_TYPE_SUBTYPE,
PURPLE_SUBTYPE_ACCOUNT),
purple_value_new(PURPLE_TYPE_STRING));

This is now done as:

purple_signal_register(handle, "account-alias-changed",
purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
PURPLE_TYPE_ACCOUNT, G_TYPE_STRING);

Similar changes have been done to the IPC mechanism in plugins.

Signal and IPC functions such as:

void purple_signal_get_values(void *instance, const char *signal,
PurpleValue **ret_value,
int *num_values, PurpleValue ***values);

are now:

void purple_signal_get_types(void *instance, const char *signal,
GType *ret_type, int *num_values,
GType **value_types);

that just return the types that the signal operates on. Results of these
functions were always used for their types only.

PurpleValue has been replaced by GType in plugins, signals and dbus.

GValue has been used to replace PurpleValue in places where values were actually
being saved - such as account settings, BList node settings, status attributes
etc.

These functions have been added to util.[ch] to create, free and duplicate
GValues (mostly used for defining status attributes):
1. purple_g_value_new()
2. purple_g_value_free()
3. purple_g_value_dup()

Now, to get rid of PurpleType, I had to have a GType corresponding to every
existing PurpleType. Thus, types that weren't already GObjects or GBoxed so far,
have been converted into GBoxed for the sake of their GType.

These entities are temporarily GBoxed, with a TODO mentioning so in their
purple_entity_get_type() function's documentation, and should eventually be
converted to GObjects:

 GBoxed
  +----PurpleCertificatePool
  +----PurpleSavedStatus
  +----PurpleStoredImage
  +----PurpleLog
  +----PurpleXfer
  +----PurplePlugin

These entities have been permanently GBoxed, as it was appropriate:

 GBoxed
  +----PurpleCertificate
  +----PurpleBuddyIcon
  +----PurpleKeyring
  +----xmlnode
  +----PurpleNotifyUserInfo

Since enums are also GEnums - and thus have GTypes, types such as
PURPLE_TYPE_CONNECTION_STATE can be used instead of PURPLE_TYPE_ENUM.

One issue with removal of PurpleValue was that it had a flag, indicating a value
as an 'outgoing' value. An outgoing value was basically a value that could be
modified by handlers. For example in signals, if a parameter was an outgoing
value of type PURPLE_TYPE_INT, it expected a pointer to a integer to be passed.

Thus, outgoing values when specified as GTypes, are G_TYPE_POINTER. Boxed values
(PURPLE_TYPE_BOXED, not GBoxed) should also use G_TYPE_POINTER. In both these
cases, in signal registrations, I have added a comment next to the parameter to
indicate the type that the pointer should point to.


Some other changes
------------------

- I have removed reserved fields from instance structs. These are unnecessary as
  it is very unlikely you would ever need to add members here. You would want to
  add members to the private structure (hidden in the API) and add functions to
  manipulate them. Class methods can be added to class structs.
- Removed purple_account_set_current_error() from the API. It is to be used
  internally by accounts.
- Made purple_notify_user_info_entry_destroy() public, so entries can be free'd
  after calling purple_notify_user_info_remove_entry().


I will be starting with redesigning the plugin API next, after which I will be
working on the protocol API. Please let me know if you have suggestions or ideas
regarding how I should pursue these.

Any feedback or suggestions for changes regarding my progress so far would be
appreciated. :)

                                                                         - Ankit



More information about the Devel mailing list