New Features in Enlightenment 22

The E22 development cycle has been underway for over a year, and it has included over 1,500 patches to address nearly 200 tickets on our issue tracker. With this has come a number of new features and improvements.

Greatly Improved Wayland Support

The majority of development for this cycle has gone towards improving Wayland support. This covers, but is not limited to: adding support for xdg-shell v6, pointer constraints, and relative pointer motion protocols. These additions improve XWayland support and increase stability across all components running under Wayland.

Continued Improvements to New Gadget Infrastructure

As previous posts have indicated, a lot of work is being done in this area. The goal is to create a more robust infrastructure with a simpler and more intuitive EFL-based API, moving away from the legacy “gadcon” interface, which has its own API and currently only functions due to mountains of gadget-specific workarounds that make safely adding any new functionality nearly impossible.
Presently, almost all gadgets and functionality that existed for the legacy gadget interface have been ported or rewritten for the new system, with future improvements already underway.

sudo/ssh Password GUI

A decision was made to implement a GUI for the ASKPASS feature of sudo/ssh which will pop up a dialog any time a password needs to be entered for these services.
More information about this feature can be found by referencing the SUDO_ASKPASS or SSH_ASKPASS environment variables.

Meson Build System

Meson is a relatively new build system that is significantly faster than autotools while providing a similar set of features. All modern distributions should be compatible with this build system, and autotools is planned to be removed after the E22 release.

Tiling Window Policy Improvements

Many issues related to this have been resolved, and some new features – such as a nicer window dragging UI – have been added.

Per-Window Pulseaudio Volume Controls

Pulseaudio exports volume controls for each audio-producing application, and work has been done to bind this into a GUI control.

New Features in Enlightenment 22 - e22-mixer

Stay Tuned for More!

More work is planned to improve the desktop experience in future versions of Enlightenment. Check out our contact page to find out more or get involved.

How to Create an EFL Gadget Sandbox

The new gadget API and infrastructure for Enlightenment continue to undergo heavy development. In addition to improving and extending the base gadget UI, work has recently begun on creating a gadget provider with the new API to provide sandboxing and allow gadgets to be written as regular applications that don’t have or require access to compositor internals.

The primary enabler of the new sandboxing system is the efl-wl compositor widget. This allows the compositor to launch applications in isolation, and also provides the ability to add protocol extensions for only that specific instance of the compositor widget. Using these features, it becomes possible to add gadget-specific protocols and utilities on the compositor side that are passed through transparently to the client gadget application.

Currently, there is one base protocol in use: the e-gadget protocol, which looks like this:



  
    
      
      
      
    
    
      
      
      
      
      
      
    
    
      
      
      
      
      
    
    
      
    
    
      
    
    
      
    
  


The purpose of this is to mimic the gadget API. Applications can choose to listen for events on the window object with the listed event name and receive the corresponding property value. This is made possible because of a shared library that is preloaded into applications by the gadget provider using the LD_PRELOAD environment variable. This preloaded library intercepts calls to create EFL window objects and converts Wayland protocol events into callbacks on the window object. Gadget application developers can then write applications without the need to concern themselves with anything related to Wayland.

Example EFL Gadget Sandbox

An example sandbox gadget written in EFL reads something like this:

#include < Elementary.h >
static Evas_Object * popup;
static Evas_Object * child;

static void
popup_del(void * data EINA_UNUSED, Evas * e EINA_UNUSED, Evas_Object * obj EINA_UNUSED, void * event_info EINA_UNUSED)
{
  popup = NULL;
}

static void
child_del(void * data EINA_UNUSED, Evas * e EINA_UNUSED, Evas_Object * obj EINA_UNUSED, void * event_info EINA_UNUSED)
{
  child = NULL;
}

static void
popup_unfocus(void * data EINA_UNUSED, Evas * e EINA_UNUSED, Evas_Object * obj, void * event_info EINA_UNUSED)
{
  evas_object_del(obj);
}

static void
mouse_button(void * data EINA_UNUSED, Evas * e EINA_UNUSED, Evas_Object * obj, void * event_info)
{
  Evas_Object * ic;
  Evas_Event_Mouse_Down * ev = event_info;
  char buf[PATH_MAX];
  int w, h;
  Evas_Object * win;
  Elm_Win_Type type = ELM_WIN_POPUP_MENU;
  if ((ev - > button != 1) && (ev - > button != 3)) return;
  if (ev - > button == 3)
  {
    type = ELM_WIN_BASIC;
    if (child)
    {
      evas_object_del(child);
      return;
    }
  } else
  {
    if (popup)
    {
      evas_object_del(popup);
      return;
    }
  }
  win = elm_win_add(elm_win_get(obj), "win", type);
  elm_win_alpha_set(win, 1);
  if (ev - > button == 3)
  {
    child = win;
    evas_object_event_callback_add(win, EVAS_CALLBACK_DEL, child_del, NULL);
  } else
  {
    popup = win;
    evas_object_event_callback_add(win, EVAS_CALLBACK_DEL, popup_del, NULL);
  }
  if (ev - > button == 3)
    evas_object_event_callback_add(win, EVAS_CALLBACK_FOCUS_OUT, popup_unfocus, NULL);
  ic = elm_icon_add(win);
  snprintf(buf, sizeof(buf), "%s/images/bubble.png", elm_app_data_dir_get());
  elm_image_file_set(ic, buf, NULL);
  elm_image_object_size_get(ic, & w, & h);
  evas_object_size_hint_aspect_set(win, EVAS_ASPECT_CONTROL_BOTH, w, h);
  if (ev - > button == 1)
  {
    elm_image_resizable_set(ic, EINA_FALSE, EINA_FALSE);
    elm_image_no_scale_set(ic, EINA_TRUE);
  }
  evas_object_size_hint_weight_set(ic, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
  evas_object_size_hint_fill_set(ic, 0.5, 0.5);
  evas_object_size_hint_min_set(ic, 100, 100);
  elm_win_resize_object_add(win, ic);
  evas_object_show(ic);
  evas_object_show(win);
}

int
main(int argc, char * argv[])
{
  Evas_Object * win, * ic;
  char buf[PATH_MAX];
  int w, h;
  elm_init(argc, (char * * ) argv);
  elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
  elm_app_info_set(main, "elementary", "images/logo.png");
  win = elm_win_add(NULL, "icon-transparent", ELM_WIN_BASIC);
  elm_win_title_set(win, "Icon Transparent");
  elm_win_autodel_set(win, EINA_TRUE);
  elm_win_alpha_set(win, EINA_TRUE);
  ic = elm_icon_add(win);
  snprintf(buf, sizeof(buf), "%s/images/logo.png", elm_app_data_dir_get());
  elm_image_file_set(ic, buf, NULL);
  elm_image_object_size_get(ic, & w, & h);
  evas_object_size_hint_aspect_set(win, EVAS_ASPECT_CONTROL_BOTH, w, h);
  if (argc > 1)
  {
    elm_image_resizable_set(ic, EINA_FALSE, EINA_FALSE);
    elm_image_no_scale_set(ic, EINA_TRUE);
  }
  evas_object_size_hint_weight_set(ic, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
  evas_object_size_hint_fill_set(ic, 0.5, 0.5);
  evas_object_size_hint_min_set(ic, 100, 100);
  elm_win_resize_object_add(win, ic);
  evas_object_show(ic);
  evas_object_event_callback_add(ic, EVAS_CALLBACK_MOUSE_DOWN, mouse_button, NULL);
  evas_object_show(win);
  ecore_main_loop_begin();
  return 0;
}

This code results in an E logo gadget like this:

How to Create an EFL Gadget Sandbox - gadget_sandbox1.png

Left clicking creates a popup window (xdg_popup) which the gadget provider extracts from the compositor widget to display like a normal gadget contextual popup.

How to Create an EFL Gadget Sandbox - gadget_sandbox2

Right clicking creates a child window (xdg_toplevel with parent) which the gadget provider extracts from the compositor widget to display like a standard gadget popup.

How to Create an EFL Gadget Sandbox - gadget_sandbox3

There are no toolkit or language restrictions for gadget applications using this functionality. The only requirement is that interacting with the compositor in any way, such as receiving the gadget properties, requires use of the Wayland protocol. Even without that, any framework or toolkit that has Wayland support can now have fully functional gadgets running inside Enlightenment.

The sandbox infrastructure can currently be found in the desksanity module, and is scheduled to be merged into the main tree after the E22 release.

Implementing a Multiseat Wayland Compositor into a Toolkit Widget

Wayland: there have been many blog posts, articles, and news items about the new Linux display protocol. There have been developers working to extend the protocol for new and extremely helpful use cases, but new frontiers continue to be explored: putting a multiseat Wayland compositor into a toolkit widget.

Due to Wayland’s philosophy of “build your own compositor,” there is no de-facto implementation of a Wayland display server that is analogous to Xorg for X11. Wayland implementations are written using the libwayland server API, and this is flexible enough to be used even in the case of a widget. Overall, the only noteworthy difference between putting a compositor in a widget and a normal nested compositor is that the output size is set to the size of the widget instead of the size of the window. With this in mind, a compositor widget can be manipulated just like any other widget, as it manages all protocol-related matters internally.

[iframe width=”560″ height=”315″ src=”https://www.youtube.com/embed/xZGYdg21IS0″ frameborder=”0″ allowfullscreen]

weston-terminal running in a widget in an X11 window.

Implementing Clipboard Sharing and Drag-N-Drop

This project had a number of difficulties along the way, most of them being related to clipboard and drag-n-drop support. A nested compositor of this type is significantly less useful if it lacks support for these things, and so it was necessary to add bridging for Wayland<->Wayland selection transfers as well as X11<->Wayland transfers. Clipboard selections were the easier part to handle in this case, as they do not require mouse/touch interactions and have no positioning data. For Wayland<->Wayland requests, all that’s needed is to transparently proxy all the data offer protocol exchanges to and from the parent compositor, and then pass any resulting file descriptors to the clients. Under X11, however, data must be manually read from or written to X11 window atoms and the selection requests need to be translated into Wayland equivalents.

[iframe width=”560″ height=”315″ src=”https://www.youtube.com/embed/o0a0I4Ao9Ng” frameborder=”0″ allowfullscreen]

Clipboard bridging under X11

Drag-N-Drop (DND) support was considerably more difficult. Not only does positioning affect the operation, but when the drag leaves the widget it becomes necessary to proxy the drag surface into a new window and initiate a global DND operation instead of working only within the widget. This leads to a new set of issues where buffer management for the drag surface must be adjusted to account for different render timings of the global drag window, which reuses the same buffer. The behavior is different between X11 and Wayland as well. Under X11, when dragging back to the originating widget, the global drag can be canceled. However, under Wayland the operation remains global and the result will be that the widget proxies a selection send through the outer compositor back to itself.

[iframe width=”560″ height=”315″ src=”https://www.youtube.com/embed/muTkLt1VNlw” frameborder=”0″ allowfullscreen]

DND bridging under X11

Adding full support for bridged DND also means that dragging between separate compositor instances also becomes possible.

[iframe width=”560″ height=”315″ src=”https://www.youtube.com/embed/lonwtDsz9VE” frameborder=”0″ allowfullscreen]

DND bridging between compositors under X11.

Another challenge with the process of putting Wayland into a canvas widget was supporting subsurfaces and other types of child surfaces. In Enlightenment, these are handled as regular objects which are stacked above the parent, but this makes the object tree more difficult to debug. The compositor widget does things a bit differently by making child surfaces into child objects of the parent surface, creating an object hierarchy which is much easier to read and debug.

[iframe width=”560″ height=”315″ src=”https://www.youtube.com/embed/u8bbTAVJirQ” frameborder=”0″ allowfullscreen]

Subsurfaces under X11.

Using the Compositor Widget to Sandbox Applications

The compositor widget has a number of useful features, but perhaps the most overlooked feature would be the ability to run applications in a sandbox. Any application launched inside has no ability to directly affect anything outside of the widget, meaning that questionable applications can be run in a safe environment from the display server’s perspective. While it has obvious uses in security, the more mundane usage could simply be embedding a web browser into an application without the need to connect to any browser engine’s embedding API.

Adding a compositor widget to an application can allow content to be embedded in isolation. It provides seamless integration with existing desktop interactions and requires no special handling to be placed into a UI layout. It also allows Wayland applications to run under X11, although this is perhaps not as useful since there are very few Wayland-only applications at present.

An Introduction to Relative and Constrained Pointers in Enlightenment

Recent development in Enlightenment’s Wayland compositor has focused on implementing cross-desktop protocols and improving stability. One of the recently handled protocol series has been relative and constrained pointers.

Relative pointer motion is a method for providing pointer movement deltas directly to applications. This is useful for a number of cases, though the easiest to imagine might be first person shooter games. In this case, the application receives movement deltas, allowing the player to endlessly scroll the screen in one direction without hitting the boundaries of the screen.

Under Enlightenment, this is handled in different ways depending on the output backend being used. For backends using libinput, e.g., DRM, it’s possible to get relative motion deltas directly from the events and then emit them for compositor use. Other backends, however, such as nested Wayland compositors, have no access to libinput’s hardware events. In this case, Enlightenment must manually calculate the deltas between motion events to pass to applications. This case is less precise, but for the case where there is no device directly attached to the compositor then there is no other method of getting relative motion data. In this way, all backends can now supply relative motion events when requested.

Pointer Confinement and Locking in Wayland

The pointer constraints protocol under Wayland consists of two separate concepts: pointer confinement and pointer locking. Pointer confinement, in this case, is where the pointer’s position is confined to a number of specified regions. Once confined, the pointer is unable to leave the specified region until the application releases the confinement or the compositor policy results in the confinement being released. Pointer locking, on the other hand, is when the pointer’s position is locked to a specific location, preventing motion events from being propagated to the client. A locked pointer similarly will not be unlocked either until the client releases the lock or the compositor policy requires the lock to end.

Confined pointers are implemented in Enlightenment primarily using calculation of motion deltas in relation to the confinement regions. By converting the deltas into simple linear equations, it becomes easy to determine whether the delta has occurred within the confined region as well as whether the pointer motion terminated within the confined region. If the motion would result in the pointer leaving the confined region, substitution into the equation allows for the calculation of the closest point to the end of the allowed confinement region, letting the pointer be confined as close to the hardware pointer position as possible.

An Introduction to Relative and Constrained Pointers in Enlightenment - confine

Locked pointers are perhaps the simplest to implement in any compositor: simply stop providing motion events to the pointer-focused surface once the lock begins.

All of these protocols are now implemented in Enlightenment as well as a number of other Wayland compositors, allowing for desktop applications to begin using the functionalities within the protocol.

Building Technical Talent in the Enlightenment Community by Solving Bugs

The past week or so has seen a significant amount of progress in the gadget backend of Enlightenment, due in no small part to the constant poking and prodding from Stephen Houston: our newest Samsung OSG Intern. As he mentioned in his post, we’ve known each other for quite some time now, so mentoring him has allowed both of us to skip over most of the pleasantries and get down to the code.

Establishing a Mutually-Beneficial Partnership

This type of internship is certainly new to me; seldom do I get the opportunity to sponsor and mentor a member of the community who has already been a contributor for such a long time. Given that I’d been the only one to use the new gadget API released in Enlightenment v21, I was curious to see what others would think after spending some time developing on top of it; soliciting feedback on the API during its development, as I did during my initial development work, hardly substitutes for the experience of spending time working with it in depth.

Before starting on his main gadget projects, Stephen decided he would try to adapt an existing gadget to the new API to test it out. We discussed this for a bit, and agreed it was a sound idea, so he set off to work. I think the first time I heard back from him was a few days after (keep in mind that he only works on this part-time in addition to his real job), and he was nearly done. He had found the new API to be satisfactory and easy to use, which was certainly a relief to me! This was, however, the point at which he brought up the sizing issue he posted a video of. We managed to work through it after a brief discussion of the problem, and he went on to tackle the last aspect of the gadget conversion: drag-n-drop (DND).

Solving Technical Problems While Growing Community Talent

Anyone who knows me or my past blogging knows that DND is not one of my favorite things to work on. While I typically have some choice words to describe it, the fact is that it’s a challenging subsystem to work within due to the necessary message passing between the drag initiator (the user) and the drop receiver. This leads to multiple places requiring debugging and testing anytime problems occur, adding to the complexity of solving problems. Solving DND bugs in a given area, however, is something like a rite of passage, and the tasks can only get easier from there… usually.

The DND issues here were numerous, and it turned out they were all either gadget infrastructure bugs or internal compositor bugs; in other words, I should be solving them to allow Stephen to continue progressing with his own work. I believe this is one of the great benefits of the way we’re running the internship program: in addition to sponsoring community members to continue doing the great work they have difficulty finding time for, we’re also providing the manpower behind the scenes to support that work and allow them to progress at their own pace, minimally hindered by any bugs outside the direct scope of their projects.

At this point, testing has been completed and Stephen’s new pager gadget has been merged into the upstream repository and is available for general use. He’s now already quite deep into the brand new launcher gadget, and I think we’ll have some exciting info to post about that soon. Stay tuned for more updates, as we have some new members of the Samsung OSG Internship program starting their projects within the next couple weeks!

Announcing the Samsung Open Source Group Internship Program

Today, I’m switching gears from my usual technical posts to introduce a new initiative that we’re rolling out at the Open Source Group: the OSG Internship Program. Since the start of our team in 2013 we’ve been involved in a number of projects in different areas of the open source ecosystem. In the process we’ve come across a number of great community members, but we’ve also realized that our team isn’t large enough to tackle all of the technical challenges that we’re facing in the open source projects we work on.

At Samsung, we greatly appreciate the collaborative nature of our relationship with these communities, and all the more so since many long-term contributors are hobbyists and students. Our appreciation runs deep for contributors who are working in these communities on their own time, particularly those who work collaboratively with us as we are preparing for future products.

From time to time, community contributors approach our team and ask how they can get more involved. We are pleased to announce that we are starting an internship program, run by the Samsung Open Source Group and focused on our strategic projects. This program will provide mentorship and compensation to qualified individuals who want to grow their skills and standing in certain communities where we need some extra helping hands.

These internships will be six months or less, and the goals are discussed and agreed upon by the Open Source Group contributor who will oversee the internship and the community member who is taking part. These high-priority work areas will likely be problem spots in the community’s codebase which have been discussed and shedcrafted into oblivion, but, as so often happens to these difficult issues which lack specific sponsorship, everyone involved has declared they don’t have the time to work on them.

We currently have a few of these planned out over the remainder of the year, and the participants will be contributing posts here to talk about their background with the project as well as the work that’s being done. Stay tuned for these developments and more!

How to Create an Enlightenment Module

Module writing is one of the primary ways to expand the functionality of Enlightenment. By dynamically loading modules, the compositor is able to import code that has access to most Enlightenment internals. This allows developers to modify the desktop environment in nearly any way they can imagine, from new gadgets to compositor effects. This article will take a look at the basics of creating an Enlightenment module.

The first part of creating a module is setting up a .desktop file for it. This allows the module to be visible for users within the module configuration dialog. An example file looks something like this:

[Desktop Entry]
Type=Link
Name=My Module
Icon=e-module-mymodule
Comment=This is the description visible to the user
X-Enlightenment-ModuleType=Category

The ‘Name’ is the user-visible name of your module, and the ‘Icon’ is the filename of the .edj file which accompanies the module. The ‘Comment’ field provides supplementary information to the user that describes the functionality of the module. ‘Category’ is the category under which the module will be listed in the module configuration. With all of these fields provided the module will be displayed to the user with all possible information available.

Next up are the required hooks in the module code. These are namespaced symbols the compositor looks for when attempting to load a module. The first such symbol is the module API declaration:

E_API E_Module_Api e_modapi =
{
   E_MODULE_API_VERSION,
   "My Module"
};

The version here is used to ensure that the module has been compiled against a compatible version of Enlightenment; older modules will be prevented from loading to avoid crashes and other unintended behaviors resulting from ABI breakages. The second member of the struct is the module’s name.

Additionally, there are three function hooks which a module provides. The first one, the init function, is mandatory:

E_API void *(*e_modapi_init)(E_Module *m);

This function runs the necessary setup calls for the module, returning a non-null value that will be attached to the module object as its data pointer. If this function returns null, the module is considered to have failed to load and appropriate error messages will be displayed automatically to the user.

Modules also have access to a special function for saving data:

E_API int (*e_modapi_shutdown)(E_Module *m EINA_UNUSED);

This function is optional. When provided, it’s called whenever Enlightenment saves its configuration so that the module may also save its own configuration. The code for this function could be as simple as:

e_config_domain_save("module.mymodule", myconfig_eet_data_descriptor_struct, myconfig_struct);

This function returns 1 for success and 0 for failure, though the value is never checked.

Lastly, modules are able to have an optional shutdown function that is called whenever the module is unloaded:

E_API int (*e_modapi_shutdown)(E_Module *m EINA_UNUSED);

If this exists, it will trigger just prior to removing the module from configuration. This function can be used to clean up any persisting data structures or objects and it returns 1 on success and 0 on failure, but this value is never used.

That concludes the basics of Enlightenment module development. For some examples of existing module code, check out the repository category on git!

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);
 eina_stringshare_del(inst->cfg->timezone);
 eina_stringshare_del(inst->cfg->time_str[0]);
 eina_stringshare_del(inst->cfg->time_str[1]);
 E_FREE(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.");
 n.urgency = E_NOTIFICATION_NOTIFY_URGENCY_NORMAL;
 e_notification_client_send(&n, NULL, NULL);
 return ECORE_CALLBACK_RENEW;
 }

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!

How Enlightenment Gadgets Handle Sizing

This tutorial will provide further detail about aspects of Enlightenment’s new gadget system. Specifically, it will explore how sizing works in different contexts and how simple sizing policies can be leveraged to provide the best view of a gadget. Let’s start with the basics: what is sizing and why does it matter?

Gadgets work a bit different than typical application widgets where one would simply pack them into a layout or use WEIGHT and ALIGN hints to fill portions of available regions. A gadget site uses an automatic sizing algorithm to fit itself into its given location. This ensures that gadgets are always the size the user has specified while also maintaining the best sizes for the gadgets so they will look the way the author intended. Finally, it also greatly simplifies the work of gadget authors since the need to calculate exact sizes and continually adjust based on available size is no longer applicable.

Example of Gadget Sizing

Looking at the relevant code from the “Start” gadget, the gadget uses the following line for its sizing:

evas_object_size_hint_aspect_set(o, EVAS_ASPECT_CONTROL_BOTH, 1, 1);

This informs the gadget site that the gadget should always maintain a 1:1 aspect for width:height, ensuring that the correct amount of space is allocated for the gadget regardless of the size at which it is displayed in a given container. The resizing can be seen in action here.

Taking a slightly more complicated case, let’s look at the clock gadget from the same video. The following function controls its sizing:

static void
_eval_instance_size(Instance *inst)
{
   Evas_Coord mw, mh;
   int sw = 0, sh = 0;
   Evas_Object *ed = elm_layout_edje_get(inst->o_clock);

   edje_object_size_min_get(ed, &mw, &mh);

   if ((mw < 1) || (mh < 1))
     {
        if (edje_object_part_exists(ed, "e.sizer"))
          {
             edje_object_part_geometry_get(ed, "e.sizer", NULL, NULL, &mw, &mh);
          }
        else
          {
             Evas_Object *owner;

             owner = e_gadget_site_get(inst->o_clock);
             switch (e_gadget_site_orient_get(owner))
               {
                case E_GADGET_SITE_ORIENT_HORIZONTAL:
                  evas_object_geometry_get(owner, NULL, NULL, NULL, &sh);
                  break;

                case E_GADGET_SITE_ORIENT_VERTICAL:
                  evas_object_geometry_get(owner, NULL, NULL, &sw, NULL);
                  break;

                default: break;
               }

             evas_object_resize(inst->o_clock, sw, sh);
             edje_object_message_signal_process(ed);

             edje_object_parts_extends_calc(ed, NULL, NULL, &mw, &mh);
          }
     }

   if (mw < 4) mw = 4;
   if (mh < 4) mh = 4;

   if (mw < sw) mw = sw;
   if (mh < sh) mh = sh;

   evas_object_size_hint_aspect_set(inst->o_clock, EVAS_ASPECT_CONTROL_BOTH, mw, mh);
}

Breaking the Code Down

The normal path for this code to take is to get the size of the layout using edje_object_size_min_get(), and then set that as the aspect for the gadget. This will trigger when the digital clock changes its face, ensuring that all the digits receive adequate space. In the case where this size has not been calculated yet, two alternate paths are provided for creating an aspect for the gadget.

In the first path, a check for the “e.sizer” part in the layout is performed and the size of this part is used if the part exists. This is a theme-defined part that may or may not exist, so checking to see which case is currently in use is important before attempting to use this size.

If this check fails, the gadget retrieves the size of the gadget site on its non-extending axis, then resizes itself to this size and forces a recalculation to make an attempt at accurate sizing. Note: in this case it is usually guaranteed that another recalculation will be triggered soon after, so this is likely to only be a temporary size.

Regardless of the method used, an aspect hint is set for the gadget that the gadget site can then use to handle any resizing that may occur. Since the gadget receives events any time a gadget site resize occurs, it’s possible for a gadget to change its aspect hints at any time to re-aspect itself and take up more or less space depending on various scenarios.

This wraps up the mechanics of gadget sizing, but stay tuned for even more gadget-related tutorials in the future!

An Introduction to Enlightenment Gadget Orientation

This article is part of a series of tutorials about Enlightenment: a compositing and stacking window manager. This tutorial will discuss gadget orientations.

Orientation is a core concept that’s vital to understanding how a gadget will be displayed to the user, and it can improve the look of gadgets while also simplifying various parts of the code. In this context, orientation can be thought of as hints the gadget owner provides to the gadget that can be used to provide a more specific view of the gadget, based on it’s location.

There are two components to orientation within the gadget system: the orientation enum and the anchor enum. Here is the orientation enum:

typedef enum
 {
 E_GADGET_SITE_ORIENT_NONE = 0,
 E_GADGET_SITE_ORIENT_HORIZONTAL,
 E_GADGET_SITE_ORIENT_VERTICAL,
 } E_Gadget_Site_Orient;

This indicates the axis on which gadgets are positioned. In a horizontal taskbar-style layout E_GADGET_SITE_ORIENT_HORIZONTAL is used, whereas E_GADGET_SITE_ORIENT_VERTICAL would indicate a vertical layout similar to the bar style in Ubuntu’s Unity environment. E_GADGET_SITE_ORIENT_NONE is different; this value indicates there is no particular policy for gadget layout. In this case, gadgets should be considered freely positioned, such as on the desktop.

Here is the anchor enum:

typedef enum
 {
 E_GADGET_SITE_ANCHOR_NONE = 0,
 E_GADGET_SITE_ANCHOR_LEFT = (1 << 0),
 E_GADGET_SITE_ANCHOR_RIGHT = (1 << 1),
 E_GADGET_SITE_ANCHOR_TOP = (1 << 2),
 E_GADGET_SITE_ANCHOR_BOTTOM = (1 << 3),
 } E_Gadget_Site_Anchor;

This is a collection of bitflags that can be combined to indicate the relative position of a gadget. For example, combining E_GADGET_SITE_ANCHOR_LEFT and E_GADGET_SITE_ANCHOR_BOTTOM would indicate that the gadget is anchored to the bottom-left part of its owner, e.g. at the bottom-left of the screen. The E_GADGET_SITE_ANCHOR_NONE value again indicates there is no anchoring, meaning the gadget can be considered as “floating.”

Putting Orientation to Use

Now, let’s look at a concrete example of both of these concepts in use in the “Start” gadget:

static void
do_orient(Instance *inst, E_Gadget_Site_Orient orient, E_Gadget_Site_Anchor anchor)
{
   char buf[4096];
   const char *s = "float";

   if (anchor & E_GADGET_SITE_ANCHOR_LEFT)
     {
        if (anchor & E_GADGET_SITE_ANCHOR_TOP)
          {
             switch (orient)
               {
                case E_GADGET_SITE_ORIENT_HORIZONTAL:
                  s = "top_left";
                  break;
                case E_GADGET_SITE_ORIENT_VERTICAL:
                  s = "left_top";
                  break;
                case E_GADGET_SITE_ORIENT_NONE:
                  s = "left_top";
                  break;
               }
          }
        else if (anchor & E_GADGET_SITE_ANCHOR_BOTTOM)
          {
             switch (orient)
               {
                case E_GADGET_SITE_ORIENT_HORIZONTAL:
                  s = "bottom_left";
                  break;
                case E_GADGET_SITE_ORIENT_VERTICAL:
                  s = "left_bottom";
                  break;
                case E_GADGET_SITE_ORIENT_NONE:
                  s = "left_bottom";
                  break;
               }
          }
        else
          s = "left";
     }
   else if (anchor & E_GADGET_SITE_ANCHOR_RIGHT)
     {
        if (anchor & E_GADGET_SITE_ANCHOR_TOP)
          {
             switch (orient)
               {
                case E_GADGET_SITE_ORIENT_HORIZONTAL:
                  s = "top_right";
                  break;
                case E_GADGET_SITE_ORIENT_VERTICAL:
                  s = "right_top";
                  break;
                case E_GADGET_SITE_ORIENT_NONE:
                  s = "right_top";
                  break;
               }
          }
        else if (anchor & E_GADGET_SITE_ANCHOR_BOTTOM)
          {
             switch (orient)
               {
                case E_GADGET_SITE_ORIENT_HORIZONTAL:
                  s = "bottom_right";
                  break;
                case E_GADGET_SITE_ORIENT_VERTICAL:
                  s = "right_bottom";
                  break;
                case E_GADGET_SITE_ORIENT_NONE:
                  s = "right_bottom";
                  break;
               }
          }
        else
          s = "right";
     }
   else if (anchor & E_GADGET_SITE_ANCHOR_TOP)
     s = "top";
   else if (anchor & E_GADGET_SITE_ANCHOR_BOTTOM)
     s = "bottom";
   else
     {
        switch (orient)
          {
           case E_GADGET_SITE_ORIENT_HORIZONTAL:
             s = "horizontal";
             break;
           case E_GADGET_SITE_ORIENT_VERTICAL:
             s = "vertical";
             break;
           default: break;
          }
     }
   snprintf(buf, sizeof(buf), "e,state,orientation,%s", s);
   elm_layout_signal_emit(inst->o_button, buf, "e");
}

This code breaks down the anchor flags to determine the gadget’s relative positioning, using the orientation to clarify positioning when the anchor information is insufficient. Based on this, the gadget is able to orient itself such that it faces away from the edge it’s positioned against. For example, when oriented horizontally along the bottom, it can display an upwards-pointing arrow, and when it is oriented vertically at the left it can show a rightward-pointing arrow.

Hopefully this tutorial has clarified orientation semantics for you. The next tutorial will cover sizing mechanics for gadgets, so stay tuned!