Page MenuHomePhabricator

Enlightenment App Spec Proposal
Updated 2,161 Days AgoPublic

This proposal tries to create a standard to handle single process applications that can handle multiple requests, spawning multiple views.

The proposed specification is based on DBus and ObjectManager Interface. It is easy to handle and would cooperate nicely with non X11 systems such as Wayland.

Requirements are based on desktop and mobile needs (as per mobile being Tizen, implemented with "aul"), but it would be nice to have it general enough to be used in other systems such as In-Vehicle Infotainment (IVI).

Structure

The application may contain a DBus bus name such as org.enlightenment.terminology, this would guarantee the single instance and auto-activation of the service if provided in an "org.enlightenment.terminology.service" file.

We desire one single process/application can implement multiple names, one example may be the telephony GUI that offers various names, such as "org.tizen.phone", "org.tizen.messages" and "org.tizen.call", while on some other platform they may be implemented by different processes. To solve this we should have a DBus ObjectPath that is not fixed, but based on the desired function. Such as /org/enlightenment/terminology. This path would be used to implement the DBus ObjectManager interface to list the children views.

The same path should implement the org.enlightenment.Application1 interface defined below:

interface org.enlightenment.Application1 {
   methods:
      CreateView(in dict arguments,
                 out objectpath childpath);
      CloseAllViews();
      Terminate();
   properties:
      readonly int PID; /* or link to some systemd? maybe system service path? */
      readonly string Title;
      readonly string IconName;
      readonly struct (u,u,b,uint8[]) IconPixels; /* width, height, has_alpha and ARGB888 */
      /* maybe consolidate other information from views? Progress, NewEvents? State? */
}

Each created or closed view will also be reported with ObjectManager, then one will get a notification of new children or those that are gone.

The childpath returned by CreateView() will always contain the parent prefix in their name, such as /org/enlightenment/terminology/tab-123, the last component can be some unique identifier, such as the shell PID in terminology, some uuid or a hash of parameters. It is up to the application to define and shouldn't be assumed by the client. The arguments is a sequence of key=value pairs that is freely defined by applications, some conventions may be used such as below:

  • argv=as would provide an array of strings in the spirit of main(int argc, char *argv[])
  • urls=as would provide an array of strings, each being one URL
  • files=as would provide an array of strings, each being one local file path
  • number=s, message=s would provide parameters for an SMS composer application

Each childpath should implement DBus Properties interface in addition to the org.enlightenment.ApplicationView1 interface defined below:

interface org.enlightenment.ApplicationView1 {
   methods:
      Close();
      Pause();
      Resume();
   properties:
      readonly string Title;
      readonly string IconName;
      readonly struct (u,u,b,uint8[]) IconPixels; /* width, height, has_alpha and ARGB888 */
      readonly int NewEvents; /* -1 = undetermined, 0 = none, >0 is a number to be displayed */
      readonly short Progress; /* -1 = unset, 0-100 is a percentage */
      readonly string State; /* live, paused, closed, ... shallow? */
      readonly int WindowId; /* needed? ugly */
   signals:
      Result(dict);
}

That is, the application view can be closed to be forgotten for once and for all, it can be paused as in it won't be needed and can drop its resources or resumed as in the resources will be used. In the mobile scenario the application is paused when user switches away from the application (goes to background), then resumed when it is used again. The close method is called when user explicitly closes the view using some window decoration or uses the task manager.

The application should be displayed to the user using the provided Title and Icon, with the Icon being a known name or path (IconName) or a uncompressed pixel data in ARGB8888 format (IconPixels), which is handy for "live icons" such as a media player that wants to show the current album cover. The icon may be overlaid by information such as NewEvents and Progress, such as new messages, missed phone calls or download status.

The State indicates the current mode, if it's live, paused, closed or shallow. The last state, closed, would be emitted only before the path disappears (notified in ObjectManager) to notify it was explicitly closed. The state shallow (keep it???) is a special kind of paused where the view is not loaded at all (ie: there is no X11Window) and once it's resumed it would need to create those (shallow could be inferred from "paused" + X11Window == 0)

Per-windowing system information is available so the window can be attached to the information, for instance raising an X11 window after Resume() or iconify it after Pause(). (this is ugly but required I'd say, unless we delegate such to the application that is even uglier)

The view resources may be deleted/unloaded when it's shallow, in that case the windowing system should keep a screenshot or some other placeholder, for all purposes user should think it's there until explicitly closed.

It may be the case that application's decide to save their state and exit, which would save precious resources such as memory. In that case the DBus name wouldn't exist, however the system should inform the user about the previous state. That means a view shouldn't be removed from user display until it goes to closed state! The window list/task manager should keep showing the application and views, even if the system is restarted/rebooted (should be persistent).

  • (raster) The above sounds like hell. especially since the dbus object will go closed briefly then disappear - race condition hell. How do we guarantee you SEE it become closed before the obj goes away? You need a specific event with the information on the state change to closed being in the event data, not in the object properties. In fact i would argue that the app has to EXPLICITLY send a "keep me around" event on exit (before deleting dbus object) to ask the wm/display to RETAIN it.

The applications are responsible to save their own state since it's hard to make this general or automatic. If they do not support this they should change every view state to closed before exiting, this would allow empty views to be loaded on next startup. The applications should restore all known/saved DBus paths on startup, usually with all views in the state shallow.

When the WM (window list/task manager) needs the window, it should access directly the bus name and path, calling org.enlightenment.ApplicationView1.Resume(). DBus activation will take care to start the application and block until it is loaded to then call Resume on the object. It is worth to mention that WM should wait the X11Window property to change before actually displaying (raise/show) the window, until then a placeholder can be used. There should be a watchdog behavior, triggered by a timeout to have this property changed.

Result(dict) signal may be used to reply with some action result, for instance a file chooser may reply with a set of selected files, a camera with library id or file path, etc.

MDI, tabs or internal views

In the case of view representing an inner view of the application, the X11Window will be the same for multiple views and application should take care to focus/display the resumed one. To make this happen the order should be to always pause before resume.

Elementary Implementation Idea

typedef Eo Elm_App_Server;
typedef Eo Elm_App_Server_View;

Elm_App_Server *elm_app_server_add(const char *packageid); /* packageid = org.enlightenment.terminology */
elm_app_server_save(Elm_App_Server *app);
elm_app_server_close_all(Elm_App_Server *app);
elm_app_server_view_append(const char *id,  Elm_App_Server_View *view, Eina_Stringshare **path); /* only will be used on Elm_App_Server_View constructor. */

Elm_App_Server events: create_view, terminate /* Should we differentiate remote request from local request? */

Elm_App_Server_View *elm_app_server_view_add(Elm_App_Server *app, const char *id); /* id = uuid, etc */
void elm_app_server_view_window_set(Elm_App_Server_View *view, Evas_Object *win); /* can be NULL to unset */
void elm_app_server_view_title_set(Elm_App_Server_View *view, const char *title); /* and getter */
void elm_app_server_view_icon_name_set(Elm_App_Server_View *view, const char *iconname); /* and getter */
void elm_app_server_view_icon_pixels_set(Elm_App_Server_View *view, unsigned w, unsigned h, Eina_Bool has_alpha, const void *pixels);  /* and getter */
void elm_app_server_view_progress_set(Elm_App_Server_View *view, char progress); /* and getter */
void elm_app_server_view_new_events_set(Elm_App_Server_View *view, int events); /* and getter */
void elm_app_server_view_pause(Elm_App_Server_View *view);
void elm_app_server_view_resume(Elm_App_Server_View *view);
Elm_App_Server_View_State elm_app_server_view_state_get(const Elm_app_Server_View *view);

Elm_App_Server_View events: resumed, paused, closed. /* Should we differentiate remote request from lcoal request? */

typedef Eo Elm_App_Client;
typedef Eo Elm_App_Client_View;
Elm_App_Client *elm_app_client_get(const char *packageid);
Eina_Bool elm_app_client_view_open(Elm_App_Client *client, const Eina_Value *args, void (*cb)(void *data, Elm_App_Client_View *view, const void *data, const char *error, const char *error_message);
const Eina_List *elm_app_client_view_list_get(Elm_App_Client *client);

Elm_App_Client events: view_created(view), view_closed(path), view_changed(view). /* more? */

void elm_app_client_view_close(Elm_App_Client_View *view, void (*cb)(void *data, const char *errmsg), const void *data);
void elm_app_client_view_pause(Elm_App_Client_View *view, void (*cb)(void *data, const char *errmsg), const void *data);
void elm_app_client_view_resume(Elm_App_Client_View *view, void (*cb)(void *data, const char *errmsg), const void *data);
const char *elm_app_client_view_title_get(const Elm_App_Client_View *view);
const char *elm_app_client_view_icon_name_get(const Elm_App_Client_View *view);
/* ... other getters ... */

Elm_App_Client_View events: state_changed, title_changed, ... /* or property_changed(string)? */

  • (raster) Naming above feels odd. elm_app_server/client... why not just elm_app_add() (instead of elm_app_server_add)? Other suggestions?

Enlightenment Implementation Idea

We must have a window manage module that handles it for mobile, and another for desktop.

The desktop case can be also spread to existing modules such as "winlist", "taskbar" and similar. It should fallback to the old fashioned (legacy) way based on the absence of evidence the application and windows support the new mode. We could use an extra .desktop key to indicate the application supports the new way and should be called via DBus rather than e_exe, such as X-EnlightenmentApplicationName=org.enlightenment.terminology. As for the windows, the view windows should contain an atom that indicates their view path E_APPLICATION_VIEW_PATH=/org/enlightenment/terminology/tab-123, those would be ignored from winlist and taskbar, being used from the DBus information. This is important so our applications would still work on other DE such as KDE and Gnome, they would call show() on window, the window would get mapped by X but the WM that supports the new protocol will manage that window properly based on the atoms, such as restoring size or even keeping it hidden if it is not the intended foreground app.

The mobile case can be done assuming all applications implement it, thus no need for legacy mode. It should persist (eet) the active applications and views, reloading on next startup. The task switcher should call Pause() on the previous application followed by Resume() on the new application. The task launcher should call Resume() on the last used view of the application if any, or CreateView() on the application if it had no views. The task manager should call Close() on the views upon user request. Both task switcher and manager should use current title, icon and other properties declared by views. When the view is brought to foreground (in use) it should call Resume() and if there is an X11Window it should be raised and made visible, if there was no such property then need to wait it to be set and then do the process. When the view is send to background it should call Pause() and the window can be iconified/hidden.

Security

Since we're using DBus we could use policykit integration to declare privileges to do certain actions, like restricting the owner of name org.enlightenment.TaskManager to call Pause(), Resume() and Close(), other's would only be able to create views and monitor property changes.

We better have the applications sandboxed in a container, as planned by systemd guys. It would take care of managing resources, capabilities and also killing them. See Linux Apps by Lennart and Greg

Resolver of Need -> Application

We should have a system that extends FreeDesktop.Org that maps needs, such as a mime-type or intent, to some application to handle it.

FreeDesktop.Org already specifies Shared Mime Info to identify the type and Mime Actions to find a handler for that type. This would work nicely for files, we could find the associated .desktop and from them the package id/application bus name and path to use and call CreateView(files=[a, b, c]) on it.

TODO: is that enough? How about needs such as dial a number, take a picture and choose a music? Should we handle this elsewhere, like define a well-known bus name for the application that takes picture? But then how to allow user define the one that should be used? It is easy to define a system close to mime-actions, relating the names to a known name that is translated to an actual name to use org.tizen.camera translates to org.tizen.basic-camera or com.company.bestcameraever.

  • (raster) I think things like dialling numbers etc. is a matter of conventions for key/value args passed to apps (services) and beyond the scope of this. also mimetypes etc. should deal with this.

TODO

  • consider apps that provides view for another apps, such as file selector, gallery image selector or camera "take picture". Maybe a single property in view that refers to another app:view is enough, like "transient" or "child".
  • should the application have a consolidated information about its views, such as icon, title, progress, new events? Automatic (inferred from views) or manually/explicitly?

References

Last Author
raster
Last Edited
Aug 22 2013, 1:18 AM
Projects
None
Subscribers
None