Black&White Project/Kiwix for Sugar/Porting
This page relates the portage or Kiwix into the Sugar platform. It might be of interest to others willing to port Xulrunner apps to Sugar.
Static
Sugar Activities are mostly distributed using the Activities Wiki, as .xo bundle. A .xo bundle is an archive containing your app. There is no dependency definition so you need to bundle everything.
This is the reason why we compiled all our XPCOM components statically for the Sugar version.
Also, Sugar does ship XulRunner at the moment but its version changes frequently. To avoid XR version conflict or removal of XR (browser development seems to have swtich to the Surf activity based on webkit), you probably want to distribute your own version of XulRunner inside your .xo file.
Sugar Activity
A Sugar activity is a folder named Kiwix.activity which resides in one of the activities placeholder (/usr/share/activities/, ~/Activities, etc). It must contain the following:
- activity/ folder
- activity/activity.info description file
- activity/activity.svg icon file
Those are the basic elements which are covered easily by sample activities.
What you need for a XR activity are:
- lib/libsugarize.so a shared link library used to proxy X calls to Sugar.
Available at [1]
- bin/SugarKiwix a bash script which will LD_PRELOAD the library and launch XR
#!/bin/sh while [ -n "$2" ] ; do case "$1" in -b | --bundle-id) export SUGAR_BUNDLE_ID="$2" ;; -a | --activity-id) export SUGAR_ACTIVITY_ID="$2" ;; -o | --object-id) export SUGAR_OBJECT_ID="$2" ;; -u | --uri) export SUGAR_URI="$2" ;; *) echo unknown argument $1 $2 ;; esac shift;shift done export LD_PRELOAD="$SUGAR_BUNDLE_PATH/lib/libsugarize.so" export NET_WM_NAME="Kiwix" exec $SUGAR_BUNDLE_PATH/kiwix
- setup.py not mandatory. A python script allowing easy packaging in .xo file.
#!/usr/bin/env python from sugar.activity import bundlebuilder bundlebuilder.start()
- activity/activity.info
[Activity] name = Kiwix bundle_id = org.kiwix.Kiwix exec = sugarKiwix icon = activity-kiwix activity_version = 1 show_launcher = yes
Activity Icon
Activity Icon must respond to very specific requirements:
- Icon should be made from a 75px square frame and fit properly
- Icon is composed of two elements: strokes and fills.
- All strokes must be same color, all fills same color.
- Stroke color and Fill color will be dynamically changed so don't choose something else than black/white.
- Strokes must be 3.5px large.
When running under Sugar, background is transparent, all strokes gets one color and all fills another.
It is important that both stroke and fills exists as the colors reflect the user interacting with the activity.
Kiwix Icon is composed of strokes and fills
- one stroke to shape the bird
- one stroke to shape the ball
- one stoke to represent the eye.
- one fill inside the bird
- one fill inside the ball
Build .XO
Building the .xo file is easy.
- prepare your folder so that it's named Kiwix.activity
- Inside this folder, place your application.ini, chrome, xulrunner, etc.
- On that same folder, place the activity subfolder, setup.py, lib/ and bin/
- launch xo build script
cd Kiwix.activity python ./setup.py dist_xo
That will create a Kiwix-x.xo in the dist/ sub folder. x represents the build version. This version number is specified in your activity/activity.info file. It is important as it's the only way to express versions in the Sugar world: the Journal and the Activities Manager.
XR App Customization
If you followed the previous steps, you now have a working Sugar activity. It launches in full screen and is able to quit.
Now, in order to comply with the Sugar HIG, you would consider some of the following UI adjustment. The easiest way to do so is by:
- Using a dedicated skin and set it as default for your Sugar build
- Using switches in your Javascript code.
You can detect a Sugar client with the following:
isSugar: function() { var environment = Components.classes["@mozilla.org/process/environment;1"].getService(Components.interfaces.nsIEnvironment); return environment.exists("SUGAR_BUNDLE_PATH"); }
U.I Adjustments
- Remove menubar. There is no menubar in Sugar apps.
- Remove Status bar.
- Fix the resolution.
The OLPC XO has a very high definition (up to 200dpi). Since XR exports the system's dpi to its rendering engine, it makes all your UI look over-sized. Fix this with the following property (adjust as you want) on ~defaults/preferences/preferences.js
pref("layout.css.dpi", 96);
- Use toolbars only. The standard way of displaying an App on Sugar is one toolbar at top (with possible sub-toolbars) and the content area on the rest of the screen.
- Toolbar at top.
Native Activity
An alternative to spending a lot of time adjusting your U.I, is to write a Sugar activity from scratch and embed Gecko. If you don't use much of XUL/Javascript for your interface, it's a good solution as it gives you native widgets.
We did not go this way as we have a lot of U.I code and we can't afford to maintain duplicate code in separate languages (Sugar activities are to be written in python).
Toolbars
Toolbars are the main way user will interact with the different features of the App. While there is no static rule about what should be in there, you might consider the following:
- Quit button at the end of the bar. The button could raise the activity logger dialog to record a session and assign meta data.
- Stock icons are available in SVG format with URLs like: moz-icon://stock/activity-stop?size=toolbar. Note that if you need to mix stock sugar icon with another one you made, you will have to switch to bitmaps for all.
Deep integration
If you need to integrate deeper with sugar (create sessions or other journal entries for example), you would have to write a component for that purpose. Since Sugar libraries are only available on Python for now, you would need to write a python component and use pyxpcom.
Warning: A bunch of Sugar API relies on Glib and a window which you won't have control over (I think).
- Write your component. Just like a C++ component, write the IDL[2] file and generate the xpt. Example python component [3] using sugar:
#!/usr/bin/env python # encoding=utf-8 from xpcom import components, verbose class SugarBridge: _com_interfaces_ = components.interfaces.ISugarBridge _reg_clsid_ = "{080787ae-ddff-11e0-aa79-000c2983f4e1}" _reg_contractid_ = "@kiwix.org/SugarBridge" def __init__(self): print("sugar bridge started") self.testAttr = "nop" def test(self): try: from sugar import env print(u"Activities: %s" % env.get_user_activities_path()) except Exception as e: print(u"oops: %r" % e)
- register it in chrome.manifest like a JS component.
component {080787ae-ddff-11e0-aa79-000c2983f4e1} components/sugar_bridge.py contract @kiwix.org/SugarBridge {080787ae-ddff-11e0-aa79-000c2983f4e1}
- Install pythonext [4]. Extract the extension in your extensions/ directory. You can just place the XPI inside and launch XR with EnableExtensionManager=1 to do so.
- create a pylib directory on your app root.
- copy/link any external python lib inside that pylib/ folder.
ln -s /usr/share/pyshared/sugar pylib/
- Use it like any other component
if (Components.classes["@kiwix.org/SugarBridge"] == undefined) dump("Unable to register the SugarBridge XPCOM, Kiwix won't be optimized for Sugar. "); else { var sugar = Components.classes["@kiwix.org/SugarBridge"] .createInstance(Components.interfaces.ISugarBridge); sugar.test(); }