Enlightenment Gadget Lifetime Management & Site Creation

This article is part of a series of tutorials about Enlightenment: a compositing and stacking window manager. The previous tutorials covered the basics of gadgets, this article will explore some of the more complex aspects in more detail, specifically lifetime management and gadget site creation.

Gadget Lifetime Management

Gadgets are bound by two lifetimes: the gadget object’s lifetime, and the gadget instance lifetime.

The gadget object is the visible display for a gadget and it is deleted when either the site is deleted, when the gadget instance is deleted, or when the gadget’s orientation changes. This lifetime can be tracked using the EVAS_CALLBACK_DEL callback on the created object. At the time of calling, this indicates that any memory related to the gadget object should be cleaned up, and any non-Elementary sub-objects should be deleted; the toolkit will not delete these automatically and they will leak without manual deletion. Once this callback has been triggered, the gadget object is dead and there will be no further calls made upon it.

The gadget instance is a sort of configuration struct for a gadget. It can be anything from a clock gadget’s time format to the wireless network a gadget is configured to connect to. The “gadget_removed” smart callback can be used on the gadget site object to terminate its lifetime. This callback occurs with the event_info parameter set as the gadget’s object when the user removes the gadget instance and typically means the gadget configuration should be deleted. However, it’s possible for gadgets to share the same configuration across multiple instances depending on how the gadget code is written. Here’s a sample “gadget_removed” callback from the clock gadget.

static void
 _clock_gadget_removed_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info)
 Instance *inst = data;

if (inst->o_clock != event_info) return;
 time_config->items = eina_list_remove(time_config->items, inst->cfg);

The callback filters out occurrences for other gadgets then deletes the configuration for the appropriate gadget instance, ensuring the gadget has been removed. Take note that this callback is called before the EVAS_CALLBACK_DEL callback, meaning any structs needed for that callback should not be freed during the “gadget_removed” callback.

Gadget Site Creation

Gadget site creation involves placing gadget sites on alternative objects; there are currently two methods for adding new gadget sites:

E_API Evas_Object *e_gadget_site_add(E_Gadget_Site_Orient orient, const char *name);
 E_API Evas_Object *e_gadget_site_auto_add(E_Gadget_Site_Orient orient, const char *name);

The former, e_gadget_site_add(), directly adds a new gadget site to the compositor canvas using the give “name” parameter as the site’s ID; the site can then be manipulated as a normal object. Calling the function again with the same name will create the same site with the same configuration. However, it’s important to note that the gadget site will not remember its geometry or stacking when using this, so ideally only container objects should use it, such as the Bryce container object here.

The latter, e_gadget_site_auto_add(), adds a configuration for a gadget site that’s automatically created and managed. The “name” parameter in this case should match the name of a compositor object (extant or not). This method will cause a gadget site to be created and positioned automatically to cover the named compositor object every time the target object is created; it will also be destroyed if the target object is deleted. This can be used to avoid the requirement that an object manages its own gadget site when the stacking and geometry would be used in this manner. As an example, this method is used to handle the editing of desklock gadgets:

static Eina_Bool
 _gadget_desklock_handler(void *d EINA_UNUSED, int t EINA_UNUSED, E_Event_Comp_Object *ev)
 E_Notification_Notify n;
 const char *name;
 Evas_Object *site, *comp_object;

name = evas_object_name_get(ev->comp_object);
 if (!name) return ECORE_CALLBACK_RENEW;
 if (strncmp(name, "desklock", 8)) return ECORE_CALLBACK_RENEW;
 evas_object_layer_set(ev->comp_object, DESKLOCK_DEMO_LAYER - 1);
 site = e_gadget_site_auto_add(E_GADGET_SITE_ORIENT_NONE, name);
 evas_object_smart_callback_add(site, "gadget_moved", _gadget_moved, NULL);
 evas_object_layer_set(site, DESKLOCK_DEMO_LAYER);
 comp_object = e_gadget_site_edit(site);
 e_comp_object_util_del_list_append(ev->comp_object, comp_object);
 e_comp_object_util_del_list_append(ev->comp_object, desklock_rect);

memset(&n, 0, sizeof(E_Notification_Notify));
 n.timeout = 3000;
 n.summary = _("Lockscreen Gadgets");
 n.body = _("Press Escape or click the background to exit.");
 e_notification_client_send(&n, NULL, NULL);

When the dummy desklock object is created, a gadget site will be created to cover it and further begin editing on it. However, note the important part here is this line:

site = e_gadget_site_auto_add(E_GADGET_SITE_ORIENT_NONE, name);

This will create the configuration for the named desklock object, ensuring that gadget internals will create the same gadget site any time the desklock object exists.
Using either of these functions, it’s possible to easily create a gadget site at any location, and have it either self-manage or perform management using other objects. This should allow gadgets to become more ubiquitous within the desktop, making them simple to place into new locations such as menus or window borders.

This concludes the tutorials for gadget lifetime management and creating new gadget sites. Stay tuned for more Enlightenment tutorials!

Author: Mike Blumenkrantz

Mike is the release manager for Enlightenment as well as a core developer of the EFL toolkit.