Canonical Voices

Posts tagged with 'programming'

Michael Hall

Since almost all of recommended packages in my original post have been tackled already thanks to Nekhelesh Ramananthan and David Baucum, here’s a new list for those of you who are still itching to knock a few out.

This list contains some of the most popular downloads from the Ubuntu Software Center that don’t have a Quicklist in Precise.  Not all of them will have useful command line options to make shortcuts for, so if you run across one of those just mention it in the comments and I’ll take it off the list.

Available

Done

Not Available

  • firefox doesn’t properly handle command line options
  • vlc doesn’t support command line options
  • skype does not support command line options
  • gparted only supports 1 commandline option which is useless
  • gtk-recordmydesktop does not support command line options
  • openshot only supports 1 commandline option which is useless
  • eclipse does not support command line options
  • ubuntuone-control-panel needs dynamic quicklist (required coding)

Jorge Castro has also linked to an AskUbuntu page that lists many pre-made Quicklists for various apps that just need to be made in a bzr branch and turned into a merge proposal, which is another great and simple way to contribute to Ubuntu.

Read more
Michael Hall

Last night I posted about how non-developers can directly contribute to the Unity desktop experience, and this morning I was greated by not one, but two contributions already made by Nekhelesh Ramananthan.

First he added player controls to Totem, then he added shortcuts for Update Manager and PPA management to the Software Center launcher. That is some awesome work.

Thanks Nekhelesh!

Read more
Michael Hall

So you want to contribute to Ubuntu’s Unity desktop, but you’re not a software developer?  No problem, there are still plenty of things you can do.  And not just in terms of documentation and translations either, there are ways to contribute directly to the desktop without having to know any programming languages.  One of these is adding Quicklists to application launcher.

Quicklists can be added dynamically from within the program code, but they can also be defined statically outside of it, in a simple text file.  It’s these static Quicklists that anybody can contribute.

For this post, I’m going to walk through the process of adding a Quicklist to Geany, my personal programming editor of choice.  You can add one for your favorite app, of choose from one of the following popular applications that are in need of a Quicklist:

IMPORTANT! Leave a comment before you start on one of these, we has 2 people working on a Brasero Quicklist because of a lack of communication.
If you chose one of these, be sure to update the linked bug report with your work.  If you choose something different, it would be a good idea to file a bug for adding a Quicklist.  Either way, I’d like to know what you’re doing, so please leave a comment on this post.

Step 1: Getting the package code

Everything in Ubuntu exists in bzr, which makes getting the source for the package easy.  just “bzr branch ubuntu:<project>”.  For geany, this is what I ran:

bzr branch ubuntu:geany

 Step 2: Add your Quicklist items

The first think you need to do is locate the .desktop file for your application.  For me, it was located in the root of the branch in a file called “geany.desktop.in”.  If you don’t see it in the root of your project’s branch, try running this command:

find ./ -name "*.desktop*"

This may not look exactly like the file in your /usr/share/applications/, since some processing is done to add translated strings for the application name and comments.  But as long as you are just adding the Quicklist items to the bottom of the file you shouldn’t have to worry about that.

The next step is to add your Quicklist shortcuts following this specification:

mhall@mhall-laptop:~/projects/Ubuntu/unity/quicklists/geany$ bzr diff
=== modified file 'geany.desktop.in'
--- geany.desktop.in 2011-05-28 19:49:19 +0000
+++ geany.desktop.in 2012-02-22 01:18:55 +0000
@@ -10,3 +10,9 @@
Categories=GTK;Development;IDE;
MimeType=text/plain;text/x-chdr;text/x-csrc;text/x-c++hdr;text/x-c++src;text/x-java;text/x-dsrc;text/x-pascal;text/x-perl;text/x-python;application/x-php;application/x-httpd-php3;application/x-httpd-php4;application/x-httpd-php5;application/xml;text/html;text/css;text/x-sql;text/x-diff;
StartupNotify=true
+Actions=New
+
+[Desktop Action New]
+Name=Open a New Instance
+Exec=geany --new-instance
+

(UPDATE 2012-02-28: A new XDG spec has been approved to make Quicklists desktop agnostic.  The Unity documentation has the new examples, and I have update the snippet above to match.)

As you can see in the example above, there isn’t much you need to do to add a Quicklist shortcut.  Calling the application’s binary with a different argument (as I did here with –new-instance) is a common and easy shortcut.  You can usually find all the available arguments to your application by calling with with –help.

 Step 3: Submitting your changes

Now that you’ve made your changes, we need to get them back into the main package.  Chances are you don’t have permission to apply them directly (otherwise you wouldn’t need this tutorial), so instead you’re going to put it somewhere else.

bzr commit -m "Add a Unity Quicklist"
bzr push lp:~mhall119/ubuntu/precise/geany/add_quicklist

This will put your changes on Launchpad in a place that the people who actually can apply it to the main packages can see your work.  But just because they can see it doesn’t mean they will see it, at least not without a little prompting from you.

To open the page on Launchpad that you just created (with your bzr push), run the following:

bzr lp-open

On that page you’ll see a link labeled “Propose for merging”, click that and fill out the form on the next page to create your merge proposal.

Step 4: Recompiling your kernel

Just kidding, there is no step 4.  You’re done!  You’ve contributed to making Ubuntu and Unity a better experience for millions of users.  Congratulations, and thank you!

 

Read more
Michael Hall

One of the most requesting things since I first introduced Singlet was to have a Quickly template for creating Unity Lenses with it.  After weeks of waiting, and after upgrading Singlet to work in Precise, and getting it into the Universe repository for 12.04, I finally set to work on learning enough Quickly internals to write a template.

It’s not finished yet, and I won’t guarantee that all of Quickly’s functions work, but after a few hours of hacking I at least have a pretty good start.  It’s not packaged yet, so to try it out you will need todo the following:

  1. bzr branch lp:~mhall119/singlet/quickly-lens-template
  2. sudo ln -s ./quickly-lens-template /usr/share/quickly/templates/singlet-lens
  3. quickly create singlet-lens <your-lens-project-name>
  4. cd <your-lens-project-name>
  5. quickly package

Read more
Michael Hall

Starting today at 1500 UTC, we’ll be conducting a series of online classes for Ubuntu Developer Week.  Whether you are interest in developing new applications for Ubuntu, or want to make an existing app take advantage of all of Ubuntu’s features, this is definitely something you should attend.

This cycle Daniel Holbach will kick things off with a overview of Ubuntu development, using Bazaar and Launchpad to collaborate both online and off with teams of developers all over the world.

After that I will be giving an overview of the unique collection of technologies and services that Ubuntu offers application developers, including Unity integration, Ubuntu One cloud storage, and the Software Center.  Then I will be joined by Micha? Sawicz to talk about Ubuntu TV, and how you can get a development environment setup and start hacking on it yourself

Later, David Callé and Michal Hruby will be showing you how to integrate with the Unity Dash by writing custom lenses and scopes for your content.  And if you are interested in that, be sure to come back Thursday for my session on writing simple lenses and scopes in Python using the Singlet library.

Mark Mims and Dustin Kirland will both by presenting on different ways Ubuntu lets you take advantage of the latest cloud technology to improve the development, testing and deployment of your application and stack.  And Stuart Langridge will be talking about the latest developments in the Ubuntu One Database (U1DB), and then showing how you can integrate our file and data syncing infrastructure into your own application.

You will also learn how to work upstream with Debian (both pulling changes in and sending them back), how to properly and easily package your application for distribution, and of course how to work on contributing changes back to Ubuntu itself.

Read more
Michael Hall

If you have written or know how to write a Quickly template, I’d like to get some help making one for Singlet Lenses and Scopes.

Read more
Michael Hall

I’ve finally had a little extra time to get back to working on Singlet.  There’s been a lot of progress since the first iteration.  To start with, Singlet had to be upgraded to work with the new Lens API introduced when Unity 5.0 landed in the Precise repos.  Luckily the Singlet API didn’t need to change, so any Singlet lenses written for Oneiric and Unity 4 will only need the latest Singlet to work in Precise[1].

The more exciting development, though, is that Singlet 0.2 introduces an API for Scopes.  This means you can write Lenses that support external scopes from other authors, as well as external Scopes for existing lenses.  They don’t both need to be based on Singlet either, you can write a Singlet scope for the Music Lens if you wanted to, and non-Singlet scopes can be written for your Singlet lens.  They don’t even have to be in Python.

In order to make the Scope API, I chose to convert my previous LoCo Teams Portal lens into a generic Community lens and separate LoCo Teams scope.  The Lens itself ends up being about as simple as can be:

from singlet.lens import Lens, IconViewCategory, ListViewCategory 

class CommunityLens(Lens): 

    class Meta:
        name = 'community'
        description = 'Ubuntu Community Lens'
        search_hint = 'Search the Ubuntu Community'
        icon = 'community.svg'
        category_order = ['teams', 'news', 'events', 'meetings']

    teams = IconViewCategory("Teams", 'ubuntu-logo')

    news = ListViewCategory("News", 'news-feed')

    events = ListViewCategory("Events", 'calendar')

    meetings = ListViewCategory("Meetings", 'applications-chat')


As you can see, it’s really nothing more that some meta-data and the categories.  All the real work happens in the scope:

class LocoTeamsScope(Scope):

    class Meta:
        name = 'locoteams'
        search_hint = 'Search LoCo Teams'
        search_on_blank = True
        lens = 'community'
        categories = ['teams', 'news', 'events', 'meetings']

    def __init__(self, *args, **kargs):
        super(LocoTeamsScope, self).__init__(*args, **kargs)
        self._ltp = locodir.LocoDirectory()
        self.lpusername = None

        if os.path.exists(os.path.expanduser('~/.bazaar/bazaar.conf')):
            try:
                import configparser
            except ImportError:
                import ConfigParser as configparser

            bzrconf = configparser.ConfigParser()
            bzrconf.read(os.path.expanduser('~/.bazaar/bazaar.conf'))

            try:
                self.lpusername = bzrconf.get('DEFAULT', 'launchpad_username')
            except configparser.NoOptionError:
                pass

    def search(self, search, model, cancellable):


I left out the actual search code, because it’s rather long and most of it isn’t important when talking about Singlet itself.  Just like the Lens API, a Singlet Scope uses an inner Meta class for meta-data.  The most important fields here are the ‘lens’ and ‘categories’ variables.  The ‘lens’ tells Singlet the name of the lens your scope is for.  Singlet uses this to build DBus names and paths, and also to know where to install your scope.  The ‘categories’ list will let you define a result item’s category using a descriptive name, rather than an integer.


 model.append('http://loco.ubuntu.com/events/%s/%s/detail/' % (team['lp_name'], tevent['id']), team['mugshot_url'], self.lens.events, "text/html", tevent['name'], '%s\n%s' % (tevent['date_begin'], tevent['description']), '')

It’s important that the order of the categories in the Scope’s Meta matches the order of categories defined in the Lens you are targeting, since in the end it’s still just the position number that’s being passed back to the Dash.

After all this, I still had a little bit of time left in the day.  And what good is supporting external scopes if you only have one anyway?  So I spent 30 minutes creating another scope, one that will read from the Ubuntu Planet news feed:

The next step is to add some proper packaging to get these into the Ubuntu Software Center, but you impatient users can get them either from their respective bzr branches, or try the preliminary packages from the One Hundred Scopes PPA.

[1] Note that while lenses written for Singlet 0.1 will work in Singlet 0.2 on Precise, the reverse is not necessarily true.  Singlet 0.2, as well as lenses and scopes written for it, will not work on Oneiric.

Read more
Michael Hall

In an effort to increase the exposure of the work being done to improve the Unity desktop, we are moving discussions from the code-named #ayatana channel on freenode to the more discoverable #ubuntu-unity channel (still on freenode).  If you want to talk to Unity developers, find out what’s happening, or join the growing ranks of community contributors, this is a good place to start.

Read more
Michael Hall

By now you should have heard that Canonical is branching out from the desktop and has begun work on getting Ubuntu on TVs.   Lost in all the discussion of OEM partnerships and content distribution agreements is a more exciting (from my perspective) topic: Ubuntu TV shows why Unity was the right choice for Canonical to make.

The Unity Platform

Ubuntu TV doesn’t just look like Unity, it is Unity.  A somewhat different configuration, visually, from the desktop version, but fundamentally the same.  Unity isn’t just a top panel and side launcher, it is a set of technologies and APIs: Indicators, Lenses, Quick Lists, DBus menus, etc.  All of those components will be the same in Ubuntu TV as they are on the desktop, even if their presentation to the user is slightly different.  When you see Unity on tablets and phones it will be the same story.

The Developer Story

Having the same platform means that Ubuntu offers developers a single development target, whether they are writing an application for the desktop, TVs, tablets or phones.  There is only one notifications API, only one search API, only one cloud syncing API.  Nobody currently offers that kind of unified development platform across all form factors, not Microsoft, not Google, not Apple.

If you are writing the next Angry Birds or TweetDeck, would you want to target a platform that only exists on one or two form factors, or one that will allow your application to run on all of them without having to be ported or rewritten?

The Consumer Story

Anybody with multiple devices has found an application for one that isn’t available for another.  How many times have we wanted the functionality offered by one of our desktop apps available to us when we’re on the go?  How many games do you have on your phone that you’d like to have on your laptop too?  With Ubuntu powered devices you will have what you want where you want it.  Combine that with Ubuntu One and your data will flow seamlessly between them as well.

A farewell to Gnome 2

None of this would have been possible with Gnome 2.  It was a great platform for it’s time, when there was a clear distinction between computers and other devices.  Computers had medium-sized screens, a keyboard and a mouse.  They didn’t have touchscreens, they didn’t change aspect ratio when turned sideways.  Devices lacked the ability to install third party applications, the mostly lacked network connectivity, and they had very limited storage and processing capabilities.

But now laptops and desktops have touch screens, phones have multi-core, multi-GHz processors.  TVs and automobiles are both getting smarter and gaining more and more of the features of both computers and devices.  And everything is connected to the Internet.  We need a platform for this post-2010 computing landscape, something that can be equally at home with a touch screen as it is with a mouse, with a 4 inch and a 42 inch display.

Unity is that platform.

Read more
pitti

On my 8 hour train ride to Budapest last Sunday I finally worked on making libxklavier introspectable. Thanks to Sergey’s fast review the code now landed in trunk. I sent a couple of refinements to the bug report still, but those are mostly just icing on the cake, the main functionality of getting and setting keyboard layouts is working nicely now (see the example script).

Read more
Michael Hall

Back when I first started writing Unity lenses, I lamented the complexity required to get even the most basic Lens written and running.  I wrote in that post about wanting to hide all of that unnecessary complexity.  Well now I am happy to announce the first step towards that end: Singlet.

In optics, a “singlet” is a very simple lens.  Likewise, the Singlet project aims to produce simple Unity lenses.  Singlet targets opportunistic programmers who want to get search results into Unity with the least amount of work.  By providing a handful of Python meta classes and base classes, Singlet lets you write a basic lens with a minimal amount of fuss. It hides all of the boilerplate code necessary to interface with GObject and DBus, leaving the developer free to focus solely on the purpose of their lens.  With Singlet, the only thing a Lens author really needs to provide is a single search function.

Writing a Singlet

So what does a Singlet Lens look like?  Here is a sample of the most basic lens, which produced the screenshot above:

#! /usr/bin/python

from singlet.lens import SingleScopeLens, IconViewCategory, ListViewCategory
from singlet.utils import run_lens

class TestLens(SingleScopeLens):

    class Meta:
        name = 'test'

    cat1 = IconViewCategory("Cat One", "stock_yet")

    cat2 = ListViewCategory("Cat Two", "hint")

    def search(self, phrase, results):
        results.append('http://google.com/search?q=%s' % phrase,
                             'file',
                             self.cat1,
                             "text/html",
                             phrase, phrase, '')

        results.append('http://google.com/search?q=%s' % phrase,
                             'file',
                             self.cat2,
                             "text/html",
                             phrase, phrase, '')

if __name__ == "__main__":
    import sys
    run_lens(TestLens, sys.argv)

As you can see, there isn’t much to it.  SingleScopeLens is the first base class provided by Singlet.  It creates an inner-scope for you, and connects it to the DBus events for handling a user’s search events.  The three things you need to do, as a Lens author, is give it a name in the Meta  class, define at least one Category, and most importantly implement your custom search(self, phrase, results) method.

Going Meta

Django developers will notice a similarity between Singlet Lenses and Django Models in their use of an inner Meta class.  In fact, they work exactly the same way, though with different properties.  At a minimum, you will need to provide a name for your lens.  Everything else can either use default values, or will be extrapolated from the name.  Everything in your Meta class, plus defaults and generated values, will be accessible in <your_class>._meta later on.

Categorically easy

Again borrowing from Django Models, you add categories to your Singlet Lens by defining it in the Class’s scope itself, rather than in the  __init__ method.  One thing that I didn’t like about Categories when writing my previous lenses was that I couldn’t reference them when adding search results to the result model.  Instead you have to give the numeric index of the category.  In Singlet, the variable name you used when defining the category is converted to the numeric index for that category, so you easily reference it again when building your search results.  But don’t worry, you category objects are still available to you in <your_class>._meta.categories if you want them.

The search is on

The core functionality of a Lens is the search.  So it makes sense that the majority of your work should happen here.  Singlet will call your search method, passing in the current search phrase and an empty results model.  From there, it’s up to you to collect data from whatever source you are targeting, and start populating that results model.

You can handle the URI

Unity knows how to handle common URIs in your results, such as file:// and http:// uris.  But often times your lens isn’t going to be dealing with results that map directly to a file or website.  For those cases, you need to hook into DBus again to handle the URI of a selected result item, and return a specifically constructed GObject response.  With Singlet, all you need to do is define a handle_uri method on your Lens, and it will take care of hooking it into DBus for you.  Singlet also provides a couple of helper methods for your return value, either hide_dash_response to hide the dash after you’ve handled the URI, or update_dash_response if you want to leave it open.

Make it go

Once you’ve defined your lens, you need to be able to initialize it and run it, again using a combination of DBus and GObject.  Singlet hides all of this behind the run_lens function in singlet.utils, which you should call at the bottom of your lens file as shown in the above snippet.

Lord of the files

There’s more to getting your Lens working that just the code, you also need to specify a .lens file describing your lens to Unity, and a .service file telling dbus about it.  Singlet helps you out here too, by providing command line helpers for generating and installing these files.  Suppose the code snippet above was in a file called testlens.py, once it’s written you can run “python testlens.py make” and it will write test.lens and unity-test-lens.service into your current directory.  The data in these files comes from <your_lens>._meta, including the name, dbus information, description and icon.  After running make you can run “sudo python testlens.py install”, this will copy your code and config files to where they need to be for Unity to pick them up and run them.

More to come

You can get the current Singlet code by branching lp:singlet.  I will be working on getting it built and available via PyPi and a PPA in the near future, but for now just having it on your PYTHONPATH is enough to get started using it.  Just be aware that if you make/install a Singlet lens, you need to make the Singlet package available on Unity’s PYTHONPATH as well or it won’t be able to run.  I’ve already converted my Dictionary Lens to use Singlet, and will work on others while I grow the collection of Singlet base classes.  If anybody has a common yet simple use case they would like me to target, please leave a description in the comments.

Read more
Michael Hall

Unity certainly has been getting a lot of attention in the past year.  Love it or hate it, everybody seems to have something to say, whether it’s about the Launcher, application indicators, or the window control buttons being on the left.  But with all the talk about Unity, good and bad, one very unique aspect that hasn’t been getting nearly enough attention are Lenses.

Lenses are a central part of the Unity desktop, and anybody who’s used it will be familiar with the default Application and File lenses, maybe even the Music lens.  But there’s so much more to this technology than you might think.  In fact, David Callé has recently been spearheading an effort to build out a large number of small but incredibly useful lenses.  I first took notice of David’s work when he released his Book lens which, being a huge ebook fan, really brought home the usefulness of Unity Lenses for me.

More recently, David has been writing Scopes for the One Hundred Scopes project.  A Scope is the program that feeds results into a Lens.  While the Lens defines the categories and filters, it’s the Scopes that do the heavy lifting of finding and organizing the data that will ultimately be displayed on your Dash.  If you follow David on Google+, chances are you’ve seen him posting screenshots of one scope after another as he writes them, often multiple of them per day.

Seeing how quickly he was able to write these, I decided to dive in and try it out myself.  You can write Lenses in a variety of languages, including Python, my language of choice.  I decided to start of with something relatively easy, and something that I’ve personally been missing for a while.  I used to use a Gnome2 applet called Deskbar, which let you type in a short search word or phrase, and it presented you with search results and various other options.  Included among those was the option to lookup the word in the gnome-dictionary, and I used this option on a startlingly frequent basis.  Unfortunately Deskbar fell out of favor and development even before the switch to Gnome 3, and I’d been lacking a quick way to lookup words ever since.  So I decided that the Lens I wanted was one that would replace this missing functionality, a Dictionary lens.

My first task was to find out how to write a lens.  I checked the Ubuntu Wiki and the Unity portal, both of which offered a lot of technical information about writing lenses, but unfortunately not very much that I found helpful for someone just starting off.  In fact, I had to get a rather large amount of one-on-one help from David Callé before I could even get the most basic functionality working.

Lenses and Scopes all communicate with each other and with the Unity Dash via DBus, and for anybody not familiar with DBus this makes for a very steep learning curve.  On top of that, writing it in Python means you’ll be relying on GObject Introspection (GI), which is a very nice way of making APIs written in one language automatically available to another, but it also means you’re going to be using the lowest common denominator when it comes to language features.  I found that learning to work with these two technologies accounted for 90% or more of the time it took me to write my Dictionary lens.  Before I write another Lens or Scope, I plan on wrapping much of the DBus and GI boilerplate and wiring behind a simple, reusable set of Python classes.  I hope this will help developers, both newbies and seasoned Unity hackers, in writing simple Scopes by allowing them to focus 90% of their time on writing the code that does the actual searching.

But by the end of the day I had a working Dictionary lens.  It uses your local spellcheck dictionary, via python-enchant, to both confirm whether or not the word you typed in is spelled correctly, as well as offer a list of suggested alternatives.  I also dug through the gnome-dictionary code and found that it was pulling its definitions from the dict.org online database using an open protocol.  Using the python-dictclient I was able to query the same database, and include the start of a word’s definition in the Lens itself.

This lens turned out to be more complex than I had originally envisioned, not just for the features listed above, but also because I needed to override what happened when you clicked on an item.  When you build up your results, you have to give each item a unique URI, which is often in the form or an http:// or file:// URL that Gnome knows how to handle.  But for results that were just words, I needed to do the handling myself, which meant more DBus wiring to have the mouse click event call a local function.  From there I was able to copy the word to the Gnome clipboard or launch gnome-dictionary for viewing the full definition.

After seeing that first Lens running in my Dash, I felt an urge to try another.  Since I already had all of the DBus and GI code, I wouldn’t have to mess with all of that and I could focus just on the functionality I wanted.  Jorge Castro has been trying to get me to write a lens for the LoCo Teams Portal (formerly LoCo Directory) since the concept was first introduced to Unity under the name “Places”.  Since LTP already offers a REST/JSON api, this turned out to be remarkably simple to do.  Between the existing DBus/GI code copied from my previous lens, and an existing python client library for the LTP, I was able to get a working lens in only a couple of hours.

For item icons, you can use either a local icon, or a URL to a remote image.  For this lens, I used the team’s mugshot URL that LTP pulls from the team’s Launchpad information.  When you search, it’ll show matching Teams, as well as Events and Meetings for those teams, and any Event or Meeting that also matches your search criteria.  I’ve also added the ability for this lens to lookup your Launchpad username (by checking for it in your bazaar.conf) and defaulting it to display the LoCo Teams you are a member of, as well as their upcoming Events and Meetings.

Both the Dictionary Lens and the LTP Lens lump the Lens and Scope code in the same Python class, but it doesn’t have to be this way.  You can write a Scope for someone else’s Lens, and vice versa.  In fact, plan on separating the LTP lens into a general purpose Community Lens, with an LTP Scope feeding it results about LoCo Teams.  From there, others can write scopes pulling in other community information to be made available on the Dash.  This will also be my prototype for a Python-friendly wrapper around all of the DBus and GI work that scope writers probably don’t need to know about anyway.

Read more
Michael Hall

As promised in my previous post about my Unity Phone Mockups, here’s a look at the TV mockups I’ve been playing with over the last week, along with the reasons behind some of the designs.  Links to the source files for these mockups, as well as the mockups by other community contributors, can be found on this wiki page.

These aren’t as innovative as my phone mockups, partly because I didn’t have much of a reference to start from like I did with IOS and Android for phones.  I have a DVR from my cable company, but no HTPC or other media center software.

The TV frame and choice of movie (Big Buck Bunny) came from Alan Bell’s template.  I added a remote control to that, and added buttons as I found a need for them.  In the mockups, I’ve highlighted the buttons you would use to interact with the given screen.

The basic concept was to overlay the Unity components when the user presses the Circle-of-Friends button on the remote, but to otherwise leave whatever they are watching full screen.  On this mockup I used applications in the Launcher, and pressing the CoF would put the focus on them the same way Alt-F1 does on the desktop.  You could then navigate through them using the arrow buttons on the remote, pressing OK to make your selection.

In this alternate mockup, I replaced the applications with Unity lenses, which I think makes more sense for a TV, since you will more often than not be interested in finding content, not selecting which application to run.  Luckily Unity already supports both, so this is just a matter of what the default initial configuration should be, after that the user can put whatever they want in the Launcher.

For opening the dash I envision being able to double-tap the CoF button (or selecting a Lens).  In fact, I would like to have this on desktop Unity, since I want quick access to the Launcher more often than I want the Dash.  Once the Dash is open, you would use the remote’s arrow buttons to navigate through the items listed, again pressing OK to make your selection.

For accessing the Dash’s filters, I added the “context menu” button to the remote which, when pressed while on the Dash screen, will open the filter sidebar, letting you navigate through the filtering options using the remote’s arrows.  Pressing the context menu button again would collapse the filter sidebar and return arrow navigation to the results table.

Likewise I added a Search button to the remote to send focus to the Dash’s search field and activate the on-screen keyboard.  Text input from a remote like this will be cumbersome (unless we come up with a monstrosity of a remote like this), so we’ll want to avoid the need for this as much as possible.  But since the Dash is highly search-focused, I felt that there needed to be a mockup for this aspect.

This is a screenshot of Shotwell taken from my laptop and scaled to fit a resolution it might be at on a TV screen.  I did this to demonstrate how an existing desktop app might look and work without modification on a 10 foot interface.  Here again I added a new button to the remote, which is a kind of next/last panel or, more accurately, the tab key on a desktop keyboard.  This lets you navigate through widgets and panels on a traditional application like you can using the tab key on a desktop.

I think with some small enhancements to GTK and QT, we can allow application developers to make navigating apps this way easier.  Alternately, a new library similar to uTouch could act as an interface between remote control input and applications.

Someone pointed me to YouTube’s LeanBack interface, an HTML5 webapp designed for 10 foot displays.  This is a very promising way to deliver applications and contents to internet connected TVs, especially if we ship with a fully functional, HTML5 supporting web browser optimized for control with a remote.

Finally I wanted to show off Media Explorer, a media center application built on the same Gnome technologies as the rest of the Ubuntu desktop.

Feedback

Just like I did in my last post, I’d like to solicit discussion and feedback on these mockups, and also ask what other scenarios/mockups you would like to see.  You can either leave comments here, or join the live conversation in #ubuntu-tv on Freenode.

Read more
rvr

In the first post we described what WebKit and JavaScriptCore is, how to program a simple WebKitGTK+ application and how to extend the JavaScript functionality with a dumb (empty) class. In the second post, we extended JavaScript to enable desktop notifications. In the third post,  WebKit/JavaScriptCore were extended to access UPower properties using D-Bus. And in the fourth post, WebKit/JavaScriptCore were extended to use JavaScript callbacks for UPower methods.

In this article we are introducing another way to extend JavaScriptCore: Seed.

 

Seed: A JavaScriptCore engine

In previous articles we've seen the power of JavaScriptCore. However, its features are tied to the WebKit engine. Wouldn't be great if we could run standalone JavaScript programs using JavaScriptCore? That's exactly what Seed does. Seed is a JavaScript interpreter, able to run pure JavaScript programs. It uses WebKitGTK+, not to browse, but to run JavaScript code.

Indeed, Seed comes in two flavors:

  • As a command line interpreter (/usr/bin/seed).
  • As a C library (libseed).

As a command line, Seed is like a Python or Perl interpreter: a script is loaded and executed. However, outside the web environment, JavaScript is pretty much useless. That's why, as we did in our previous examples, Seed adds support for some native libraries. And specifically, Seed can be used to program GTK+ applications using JavaScript, thanks to its GObject Introspection/JavaScript bridge.

libseed, the C library, allows third-parties to easily build scripting capabilities for their programs. It adds a thin layer to JavaScriptCore API. The functions and methods available in JSC are usually available in libseed. This mean we can define new features and access JavaScript objects from C.

 

A simple Seed script

To install Seed in Ubuntu, type:

$ sudo apt-get install seed libseed-gtk3-dev

This is a simple GTK+ program, which just shows a window with a button.

#!/usr/bin/seed

/* Imports GTK+ libraries */
Gtk = imports.gi.Gtk;

/* Inits GTK+ */
Gtk.init(null, null);

/* Creates window */
var window = new Gtk.Window();

/* Terminates program if window is closed */
window.signal.hide.connect(Gtk.main_quit);

/* Creates button */
var button = new Gtk.Button();
button.set_label('Hello world');

/* Terminates program if button is clicked */
button.signal.clicked.connect(Gtk.main_quit);

/* Add button to window */
window.add(button);

/* Shows */
window.set_default_size(100,100);
window.show_all();

/* Main loop */
Gtk.main();

To run it:

$ seed gtk.js

This shows:

Pantallazo del 2011-11-14 01:48:08

WebViews and Seed

In our previous articles, we've extended JavaScript features manually, programming C methods to create JS classes. Those classes were available to our Web Views. But Seed already provides a lot of libraries. In the next example, we'll see how to use libseed to extend WebKit.

Seed is built upon JavaScriptCore, which is a core element of WebKit. In order to expose libseed features in a browser, we need to connect Seed with WebKit. How to do that? When a WebView is created, a JavaScriptCore's Context Object is created. This Context Object stores the status of the JavaScript engine. Generally, Seed creates is own Context Object, but there is function to use an already created context. Using this function, Seed will populate the WebView's context with Seed's libraries.

This the source code of our program which exposes Seed libraries to a WebKitGTK+ web view.

#include <stdio.h>
#include <gtk/gtk.h>
#include <webkit/webkit.h>
#include <seed.h>
#include <JavaScriptCore/JavaScript.h>

SeedEngine *engine;

static void window_object_cleared_cb(WebKitWebView  *web_view,
                                WebKitWebFrame *frame,
                                gpointer        context,
                                gpointer        arg3,
                                gpointer        user_data)

{
    JSGlobalContextRef jsContext = webkit_web_frame_get_global_context(frame);
    engine = seed_init_with_context (NULL, NULL, jsContext);
}

static void destroy_cb(GtkWidget* widget, gpointer data)
{
    g_free (engine);
    gtk_main_quit ();
}

static GtkWidget* main_window;
static WebKitWebView* web_view;

static GtkWidget* create_browser()
{
    GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

    web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
    gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (web_view));

    g_signal_connect (G_OBJECT (web_view), "window-object-cleared", G_CALLBACK(window_object_cleared_cb), web_view);

    return scrolled_window;
}

static GtkWidget* create_window()
{
    GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_default_size (GTK_WINDOW (window), 500, 500);
    g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
    return window;
}

int main (int argc, char* argv[]) {
    gtk_init (&argc, &argv);
    if (!g_thread_supported())
       g_thread_init (NULL);

    GtkWidget* vbox = gtk_vbox_new(FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox), create_browser (), TRUE, TRUE, 0);

    main_window = create_window();
    gtk_container_add(GTK_CONTAINER (main_window), vbox);

    gchar* uri = (gchar*) "file://webkit-seed.html";
    webkit_web_view_load_uri(web_view, uri);

    gtk_widget_grab_focus (GTK_WIDGET (web_view));
    gtk_widget_show_all (main_window);
    gtk_main ();

    return 0;
}

To compile it, type:

$ gcc -o webkit-seed webkit-seed.c `pkg-config --cflags --libs webkitgtk-3.0 seed`

And this is the webkit-seed.html file:

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>

    <script type="text/javascript">
    var sqlite = imports.sqlite;
    sql = new sqlite.Database('/tmp/webkit_seed_test.db')
    sql.exec('CREATE TABLE test (id INTEGER PRIMARY KEY, data TEXT)');
    sql.close();
    </script>

</body>
</html>

To run the program:

$ webkit-seed

This time, the browser will appear, but the fun action will be in the /tmp directory. There we'll found a new SQLite 3 database, with a two-field table.

Of course, Seed gives access to a lot of sensible libraries. Safety would be at risk if we let any JavaScript program to run this way, but it also opens a whole world of possibilities!

P.S.: Of course, I tried to run the GTK+ example inside the WebView. Unfortunately, it didn't work :-(

Read more
rvr

In the previous articles dedicated to WebKit, we've used the WebKitGTK+ in plain C. Thanks to GObject and GLib libraries,  doing bindings for others languages is quite easy. And of course, bindings for WebKitGTK+ exists for Python, one of my favorite programming languages. Python is ideal for rapid prototyping and/or fast development.

To use WebKitGTK+ in Python and Ubuntu, python-webkit library must be installed:

$ sudo apt-get install python-webkit

This is the source code of a simple WebKit browser in PyGTK+.

#!/usr/bin/env python
import pygtk
import gtk
import webkit

# Creates the GTK+ app
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.scrolled_window = gtk.ScrolledWindow()
# Creates a WebKit view
self.web_view = webkit.WebView()
self.scrolled_window.add(self.web_view)
self.window.add(self.scrolled_window)
# Sets the window size
self.window.set_default_size(1024, 800)
# Loads this URL
self.web_view.load_uri('http://rvr.typepad.com/')
gtk.main()

Using Python we get rid of the manual memory management. Now, let's use WebKit and Python to build a tool.

Facebook and desktop applications: OAuth 2.0.

Facebook has a programmable API, so application can interact with user data. Probably you know Farmville, the popular Facebook game. Farmville uses Facebook API to access to our data. For applications to be able to interact with our data, developers must register the application in Facebook and the each user grants permission access to the app.

This protocol to obtain user access is called OAuth 2.0, and is has become a popular standard among many popular web sites. The workflow is:

  1. The application launchs a browser and load FB's OAuth dialog, passing as arguments the app key and a redirection URL: https://www.facebook.com/dialog/oauth?client_id=YOUR_APP_ID&redirect_uri=...
  2. The user authenticates himself in Facebook.
  3. The user accepts granting access to the application.
  4. Facebook returns an access token to a callback URL.

The problem with this workflow is that desktop applications don't usually run a web server, so it's difficult to get this access token. How can we do it? Using WebKit!

Facebook states that the callback URL can be set to https://www.facebook.com/connect/login_success.html which will receive the access token as a parameter (login_sucess.html#access_token=...). We are going to use WebKit to identify this URLs using this callback:

self.web_view.connect('load-committed', self._load_committed_cb) # Load page

The 'load-commited' signal is emmited by the WebKit view when a page is about to be loaded. In the _load_commited_cb function, the page URL is checked to get Facebook's application access token. This token is finally saved to a file and the browser is closed.

This is the source code:

#!/usr/bin/env python
import pygtk
import gtk
import webkit
import urllib
import urlparse

FB_TOKEN_FILE = 'access_token.txt'

class Browser:
    """ Creates a web browser using GTK+ and WebKit to authorize a
        desktop application in Facebook. It uses OAuth 2.0.
        Requires the Facebook's Application ID. The token is then
        saved to FB_TOKEN_FILE.
    """

    def __init__(self, app_key, scope='offline_access'):
        """ Constructor. Creates the GTK+ app and adds the WebKit widget
            @param app_key Application key ID (Public).

            @param scope A string list of permissions to ask for. More at
            http://developers.facebook.com/docs/reference/api/permissions/
        """
        self.token = ''
        self.token_expire = ''
        self.scope = scope
        # Creates the GTK+ app
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.scrolled_window = gtk.ScrolledWindow()
        # Creates a WebKit view
        self.web_view = webkit.WebView()
        self.scrolled_window.add(self.web_view)
        self.window.add(self.scrolled_window)
        # Connects events
        self.window.connect('destroy', self._destroy_event_cb) # Close window
        self.web_view.connect('load-committed', self._load_committed_cb) # Load page
        self.window.set_default_size(1024, 800)
        # Loads the Facebook OAuth page
        self.web_view.load_uri(
            'https://www.facebook.com/dialog/oauth?client_id=%s&redirect_uri=%s&response_type=token&scope=%s' % (urllib.quote(app_key), urllib.quote('https://www.facebook.com/connect/login_success.html'), urllib.quote(self.scope))
            )

    def _load_committed_cb(self, web_view, frame):
        """ Callback. The page is about to be loaded. This event is captured
            to intercept the OAuth 2.0 redirection, which includes the
            access token.

            @param web_view A reference to the current WebKitWebView.

            @param frame A reference to the main WebKitWebFrame.
        """
        # Gets the current URL to check whether is the one of the redirection
        uri = frame.get_uri()
        parse = urlparse.urlparse(uri)
        if (hasattr(parse, 'netloc') and hasattr(parse, 'path') and
            hasattr(parse, 'fragment') and parse.netloc == 'www.facebook.com' and
            parse.path == '/connect/login_success.html' and parse.fragment):
            # Get token from URL
            params = urlparse.parse_qs(parse.fragment)
            self.token = params['access_token'][0]
            self.token_expire = params['expires_in'][0] # Should be equal to 0, don't expire
            # Save token to file
            token_file = open(FB_TOKEN_FILE, 'w')
            token_file.write(self.token)
            token_file.close()
            print "Authentication done. Access token available at %s" % (FB_TOKEN_FILE)
            gtk.main_quit() # Finish

    def _destroy_event_cb(self, widget):
        """ Callback for close window. Closes the application. """
        return gtk.main_quit()

    def authorize(self):
        """ Runs the app. """
        self.window.show_all()
        gtk.main()

if (__name__ == '__main__'):
    # Creates the browser
    browser = Browser(app_key='XXXXXXXXXXX', scope='offline_access,read_stream')
    # Launch browser window
    browser.authorize()
    # Token available?
    print "Token: %s" % (browser.token)

Easy, isn't it?

The complete source code is available at github.com/vrruiz/FacebookAuthBrowser

Read more
Michael Hall

It’s late, I’m tired, so this is going to be brief.  But if I didn’t put something up now, chances are I’d procrastinate to the point where it didn’t matter anymore, so something is better than nothing.

JuJu

So the buzz all week was about Juju and Charms.  It’s a very cool technology that I think is really going to highlight the potential of cloud computing.  Until now I always had people comparing the cloud to virtual machines, telling me they already automate deploying VMs, but with Juju you don’t think about machines anymore, virtual of otherwise.  It’s all about services, which is really what you want, a service that is doing something for you.  You don’t need to care where, or on what, or in combination with some other thing, Juju handles all that automatically.  It’s really neat, and I’m looking forward to using it more.

Summit

Summit worked this week.  In fact, this is the first time in my memory where there wasn’t a problem with the code during UDS.  And that’s not because we left it alone either.  IS actually moved the entire site to a new server the day before UDS started.  We landed several fixes during the week to fix minor inconveniences experienced by IS or the admins.  And that’s not even taking into consideration all the last-minute features that were added by our Linaro developers the week prior.  But through it all, Summit kept working.  That, more than anything else, is testament to the work the Summit developers put in over the last cycle to improve the code quality and development processes, and I am very, very proud that.  But we’re not taking a break this cycle.  In fact, we had two separate sessions this week about ways to improve the user experience, and will be joined by some professional designers to help us towards that goal.

Ubuntu One eBook syncing

So what started off as an casual question to Stuart Langridge turned into a full blown session about how to sync ebook data using Ubuntu One.  We brainstormed several options of what we can sync, including reading position, bookmarks, highlights and notes, as well as ways to sync them in an application agnostic manner.  I missed the session on the upcoming Ubuntu One Database (U1DB), but we settled on that being the ideal way of handling this project, and that this project was an ideal test case for the U1DB.  For reasons I still can’t explain, I volunteered to develop this functionality, at some point during the next cycle.  It’s certainly going to be a learning experience.

Friends

Friends!  It sure was good to catch up with all of you.  Both friends from far-away lands, and those closer to home.  Even though we chat on IRC almost constantly, there’s still nothing quite like being face to face.  I greatly enjoyed working in the same room with the Canonical ISD team, which has some of the smartest people I’ve ever had the pleasure of working with.  It was also wonderful to catch up with all my friends from the community.  I don’t know of any other product or project that brings people together the way Ubuntu does, and I’m amazed and overjoyed that I get to be a part of it.

Read more
Michael Hall

If you’ve been doing anything with Ubuntu lately, chances are you’ve been hearing a lot of buzz about Juju.  If you’re attending UDS, then there’s also a good chance that you’ve been to one or more sessions about Juju.  But do you know it?

The building blocks for Juju are it’s “charms”, which detail exactly how to deploy and configure services in the Cloud.  Writing charms is how you harness the awesome power of Juju.  Tomorrow (Friday) there will be a 2 hour session all about writing charms, everything from what they do and how they work, to helping you get started writing your own.  Questions will be answers, minds will be inspired, things will be made, so don’t miss out.

http://summit.ubuntu.com/uds-p/meeting/19875/juju-charm-school/

(Photo courtesy of http://www.flickr.com/photos/slightlynorth/3977607387/)

Read more
Michael Hall

Are you both an Ubuntu user and a bibliophile?  Want to keep your ebooks synced between all your connected devices, including bookmarks and reading position?   If so, join us for this UDS session Thursday, Nov 3rd, where we’ll be talking about how to add that functionality to Ubuntu One.

http://summit.ubuntu.com/uds-p/meeting/19820/other-p-u1-book-sync/

Read more
rvr

In the first post we described what WebKit and JavaScriptCore is, how to program a simple WebKitGTK+ application and how to extend the JavaScript functionality with a dumb (empty) class. In the second post, we extended JavaScript to enable desktop notifications. In the third post,  WebKit/JavaScriptCore were extended to access UPower properties using D-Bus.

 

Callbacks

As explained in the previous article, D-Bus is object oriented, and interfaces can expose objects with their methods, properties and events. We used D-Bus to access battery status, and extended WebKit/JavaScript Core to read it. In this post we are going to connect D-Bus signals with JavaScript callback functions. What we want to do is execute custom JavaScript code after a D-Bus signal has been emitted (and received).

D-Bus signals are events that clients can subscribe to. As explained, UDisks interface emits signals everytime a new external drive is attached to the system, and Linux desktops capture this events to launch file managers (and other applications).  UPower.Device also emits signals, and we are going to capture Changed().

It's easy to connect functions using  D-Bus Glib bindings. First, we add a signal to the proxy object, which is a  way to indicate the type of the arguments. Changed() doesn't has any parameter, so the call is this:

    dbus_g_proxy_add_signal (proxy, "Changed", G_TYPE_INVALID);

And then we connect the D-Bus signal with a callback function (battery_changed_cb).

    dbus_g_proxy_connect_signal(proxy,
                                "Changed",
                                G_CALLBACK(battery_changed_cb),
                                ref,
                                (GClosureNotify) g_free);

This callback function will receive two parameters, proxy and ref, with references to its correspondent JavaScriptCore context and global object.

So, when UPower updates the battery status, it emits a Changed() signal. This signal will be received by our JavaScript Battery() objects. What happens then? battery_changed_cb will check whether Battery has a property called onChange and whether is of type function. So, if Battery.onChange stores a function, battery_changed_cb will call it. You'll see it more clearly in the JavaScript file.

Above is the source code of our previous D-Bus program. Remember that is based on that of the previous example.

#include <stdlib.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>
#include <gtk/gtk.h>
#include <webkit/webkit.h>
#include <JavaScriptCore/JavaScript.h>

DBusGConnection *conn;
DBusGProxy *proxy;
DBusGProxy *properties_proxy;

typedef struct {
    JSContextRef context;
    JSObjectRef object;
} RefContextObject;

/* Callback for UPower.Device.Changed() */
static void battery_changed_cb(DBusGProxy *_proxy, RefContextObject *ref) 
{
    g_message("Battery changed");
    
    /* Get onChange property */
    JSStringRef string_onchange;
    string_onchange = JSStringCreateWithUTF8CString("onChange");
    JSValueRef func = JSObjectGetProperty(ref->context, ref->object, string_onchange, NULL);
    JSObjectRef function = JSValueToObject(ref->context, func, NULL);
    JSStringRelease(string_onchange);
    
    if (!JSObjectIsFunction(ref->context, function)) {
         g_message("JSObject is not function or is not set");
         return;
    }
        
    JSValueRef result = JSObjectCallAsFunction(ref->context, // The execution context to use
                                               function, // The JSObject to call as a function.
                                               ref->object, // The object to use as "this," or NULL to use the global object as "this."
                                               0, //  An integer count of the number of arguments in arguments.
                                               NULL, // A JSValue array of arguments to pass to the function. Pass NULL if argumentCount is 0.
                                               NULL); // A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception.
        
}

/* Class initialize */
static void battery_init_cb(JSContextRef ctx,
                            JSObjectRef object)
{
    GError *error = NULL;
    
    conn = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);

    if (conn == NULL)
    {
        g_printerr ("Failed to open connection to bus: %s\n", error->message);
        g_error_free (error);
        return;
    }

    /* Create a proxy object for "org.freedesktop.UPower" */
    proxy = dbus_g_proxy_new_for_name (conn,
                                       "org.freedesktop.UPower",
                                       "/org/freedesktop/UPower/devices/battery_BAT0",
                                       "org.freedesktop.UPower.Device");
    if (proxy == NULL)
    {
        g_printerr ("Failed to create proxy object\n");
        return;
    }
    
    /* Create a proxy object for "org.freedesktop.UPower.Properties" */
    properties_proxy = dbus_g_proxy_new_from_proxy (proxy,
                                                    "org.freedesktop.DBus.Properties",
                                                    dbus_g_proxy_get_path (proxy));
    if (properties_proxy == NULL)
    {
        g_printerr ("Failed to create proxy object\n");
        return;
    }

    /* Connect callback to signal org.freedestop.UPower.Device.Changed() */
    RefContextObject *ref = g_new(RefContextObject, 1);
    ref->context = ctx;
    ref->object = object;
    
    dbus_g_proxy_add_signal (proxy, "Changed", G_TYPE_INVALID);
    dbus_g_proxy_connect_signal(proxy,
                                "Changed",
                                G_CALLBACK(battery_changed_cb),
                                ref,
                                (GClosureNotify) g_free);
  
    error = NULL;
}

/* Class constructor */
static JSObjectRef battery_constructor_cb(JSContextRef context,
                                          JSObjectRef constructor,
                                          size_t argumentCount,
                                          const JSValueRef arguments[],
                                          JSValueRef *exception)
{
    return constructor;
}                                       

/* Class finalize */
static void battery_destroy_cb(JSObjectRef object)
{
    /* Ends Battery */
    if (proxy != NULL) g_object_unref (proxy);
    if (properties_proxy != NULL) g_object_unref (properties_proxy);
}

static gboolean proxy_property_value(char *property,
				                     GValue *get_value,
                                     GError **error)
{
    /* Call ListNames method, wait for reply */
    return dbus_g_proxy_call (properties_proxy, "Get", error,
                              G_TYPE_STRING, "/org/freedesktop/UPower/devices/battery_BAT0",
                              G_TYPE_STRING, property,
                              G_TYPE_INVALID,
                              G_TYPE_VALUE, get_value,
                              G_TYPE_INVALID);
}

static JSValueRef proxy_double_value(JSContextRef context,
                                     char *property,
                                     size_t argumentCount)
{
    GError *error = NULL;
    GValue get_value = {0, };
  
    if (argumentCount == 0) {
        /* Call method, wait for reply */
        if (!proxy_property_value(property, &get_value, &error))
        {
            g_printerr ("Error: %s\n", error->message);
            g_error_free (error);
            return JSValueMakeUndefined(context);
        }
        
        gdouble value = g_value_get_double(&get_value);
        g_value_unset(&get_value);
        return JSValueMakeNumber(context, value);
    }
    
    return JSValueMakeUndefined(context);
}

static JSValueRef proxy_uint64_value(JSContextRef context,
                                     char *property,
                                     size_t argumentCount)
{
    GError *error = NULL;
    GValue get_value = {0, };
  
    if (argumentCount == 0) {
        /* Call method, wait for reply */
        if (!proxy_property_value(property, &get_value, &error))
        {
            g_printerr ("Error: %s\n", error->message);
            g_error_free (error);
            return JSValueMakeUndefined(context);
        }
        
        guint64 value = g_value_get_uint64(&get_value);
        g_value_unset(&get_value);
        return JSValueMakeNumber(context, value);
    }
    
    return JSValueMakeUndefined(context);
}

static JSValueRef proxy_boolean_value(JSContextRef context,
                                     char *property,
                                     size_t argumentCount)
{
    GError *error = NULL;
    GValue get_value = {0, };
  
    if (argumentCount == 0) {
        /* Call method, wait for reply */
        if (!proxy_property_value(property, &get_value, &error))
        {
            g_printerr ("Error: %s\n", error->message);
            g_error_free (error);
            return JSValueMakeUndefined(context);
        }
        
        gboolean value = g_value_get_boolean(&get_value);
        g_value_unset(&get_value);
        return JSValueMakeBoolean(context, value);
    }
    
    return JSValueMakeUndefined(context);
}

/* Battery.capacity method callback implementation */
static JSValueRef battery_capacity_cb(JSContextRef context,
                                      JSObjectRef function,
                                      JSObjectRef thisObject,
                                      size_t argumentCount,
                                      const JSValueRef arguments[],
                                      JSValueRef *exception)
{
    return proxy_double_value(context, "Capacity", argumentCount);
}

/* Battery.percentage method callback implementation */
static JSValueRef battery_percentage_cb(JSContextRef context,
                                        JSObjectRef function,
                                        JSObjectRef thisObject,
                                        size_t argumentCount,
                                        const JSValueRef arguments[],
                                        JSValueRef *exception)
{
    return proxy_double_value(context, "Percentage", argumentCount);
}

/* Battery.voltage method callback implementation */
static JSValueRef battery_voltage_cb(JSContextRef context,
                                     JSObjectRef function,
                                     JSObjectRef thisObject,
                                     size_t argumentCount,
                                     const JSValueRef arguments[],
                                     JSValueRef *exception)
{
    return proxy_double_value(context, "Voltage", argumentCount);
}

/* Battery.updateTime method callback implementation */
static JSValueRef battery_update_time_cb(JSContextRef context,
                                         JSObjectRef function,
                                         JSObjectRef thisObject,
                                         size_t argumentCount,
                                         const JSValueRef arguments[],
                                         JSValueRef *exception)
{
    return proxy_uint64_value(context, "UpdateTime", argumentCount);
}

/* Battery.PowerSupply method callback implementation */
static JSValueRef battery_power_supply_cb(JSContextRef context,
                                          JSObjectRef function,
                                          JSObjectRef thisObject,
                                          size_t argumentCount,
                                          const JSValueRef arguments[],
                                          JSValueRef *exception)
{
    return proxy_boolean_value(context, "PowerSupply", argumentCount);
}

/* Class method declarations */
static const JSStaticFunction battery_staticfuncs[] =
{
    { "capacity", battery_capacity_cb, kJSPropertyAttributeReadOnly },
    { "percentage", battery_percentage_cb, kJSPropertyAttributeReadOnly },
    { "voltage", battery_voltage_cb, kJSPropertyAttributeReadOnly },
    { "updateTime", battery_update_time_cb, kJSPropertyAttributeReadOnly },
    { "powerSupply", battery_power_supply_cb, kJSPropertyAttributeReadOnly },
    { NULL, NULL, 0 }
};

static const JSClassDefinition notification_def =
{
    0,                     // version
    kJSClassAttributeNone, // attributes
    "Battery",             // className
    NULL,                  // parentClass
    NULL,                  // staticValues
    battery_staticfuncs,   // staticFunctions
    battery_init_cb,       // initialize
    battery_destroy_cb,    // finalize
    NULL,                  // hasProperty
    NULL,                  // getProperty
    NULL,                  // setProperty
    NULL,                  // deleteProperty
    NULL,                  // getPropertyNames
    NULL,                  // callAsFunction
    battery_constructor_cb, // callAsConstructor
    NULL,                  // hasInstance  
    NULL                   // convertToType
};

/* Callback - JavaScript window object has been cleared */
static void window_object_cleared_cb(WebKitWebView  *web_view,
                                     WebKitWebFrame *frame,
                                     gpointer        context,
                                     gpointer        window_object,
                                     gpointer        user_data)

{
    /* Add classes to JavaScriptCore */
    JSClassRef classDef = JSClassCreate(&notification_def);
    JSObjectRef classObj = JSObjectMake(context, classDef, context);
    JSObjectRef globalObj = JSContextGetGlobalObject(context);
    JSStringRef str = JSStringCreateWithUTF8CString("Battery");
    JSObjectSetProperty(context, globalObj, str, classObj, kJSPropertyAttributeNone, NULL);
}

/* Destroy callback */
static void destroy(GtkWidget *widget,
                    gpointer   data )
{
    gtk_main_quit();
}

int
main (int argc, char* argv[])
{
    /* Initialize the widget set */
    gtk_init (&argc, &argv);
    
    /* Create the window widgets */
    GtkWidget *main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    GtkWidget *scrolled_window = gtk_scrolled_window_new (NULL, NULL);
    
    /* Create the WebKit Web View widget */
    GtkWidget *web_view = webkit_web_view_new ();
    
    /* Connect the window object cleared event with callback */
    g_signal_connect (G_OBJECT (web_view), "window-object-cleared", G_CALLBACK(window_object_cleared_cb), web_view);


    /* Place the WebKitWebView in the GtkScrolledWindow */
    gtk_container_add (GTK_CONTAINER (scrolled_window), web_view);
    gtk_container_add (GTK_CONTAINER (main_window), scrolled_window);

    /* Connect the destroy window event with destroy function */
    g_signal_connect (G_OBJECT (main_window), "destroy", G_CALLBACK (destroy), NULL);
    
    /* Open webpage */
    webkit_web_view_load_uri (WEBKIT_WEB_VIEW (web_view), "file://webkit-05.html");

    /* Create the main window */
    gtk_window_set_default_size (GTK_WINDOW (main_window), 800, 600);
    
    /* Show the application window */
    gtk_widget_show_all (main_window);

    /* Enter the main event loop, and wait for user interaction */
    gtk_main ();
    
    /* The user lost interest */
    return 0;
}

To compile it on Ubuntu:

$ gcc webkit-05.c -o webkit.c `pkg-config --cflags --libs webkitgtk-3.0 dbus-glib-1`

Above, the corresponding HTML file webkit-05.html.

<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
  <h1>Extending JavaScript with WebKit. Battery class.</h1>
  <div id="battery-status"></div>
  <script type="text/javascript">
    var display = function () {
        var div = document.getElementById("battery-status");
        var battery_status = "Capacity: " + Battery.capacity() + "<br>";
        battery_status += "Percentage: " + Battery.percentage() + "%<br>";
        battery_status += "Voltage: " + Battery.voltage() + "<br>";
        battery_status += "Update Time: " + Battery.updateTime() + "<br>";
        battery_status += "Power supply: " + Battery.powerSupply() + "<br>";
        div.innerHTML = battery_status;
    }
  
    var battery = new Battery();
    battery.onChange = display;
    display();
  </script>
</body>
</html>

var display stores a reference to a function. This function fills the empty <div id=battery-status> with the actual battery status. Finally, a Battery() object is created and battery.onChange is defined.

If we run our program

$ ./webkit-05

when UPower.Device.Changed() is emitted, our JavaScript function will be called and the battery status, automatically updated in the web page.

Pantallazo del 2011-11-02 19:15:45
As before, the complete source code is available at github.com/vrruiz/WebKit-JavaScriptCore-Extensions.

Read more
Michael Hall

In a previous post a commentator was explaining his typical web-stack deployment, and boasting about how he “can roll this out on Debian in less than 4 hours”.  Now he’s talking about provisioning, installing the OS, installing the services, and configuring everything.  That’s easily a day’s work for a capable sysadmin in a corporate environment. At least, it is in my experience.  So 4 hours sounds pretty good, doesn’t it?  I tell you what, go find your nearest admin and ask them how long it would take for them to get a new WordPress site up and ready for you to start posting to.  No really, I mean it, go ask.  I’ll wait.

Back?  Good.  I’m betting that, for many of you, the answer was a day or less, depending on their backlog of work.  A lucky few would be able to get their new site in an hour or two.  Not bad. But if you were using Ubuntu and cloud technology, you would have had your new site ready in the time that it took you to get that answer.

You think I’m exaggerating?  You can get a new world-accessible WordPress site up and running in less than 10 minutes.  That’s not marketing hype, Canonical is quite literally putting it’s money where it’s mouth is, by paying for an hour of Ubuntu Server on Amazon’s cloud service.  Go to http://try.cloud.ubuntu.com and follow along, and you’ll have a WordPress site up and running in less time than it’ll take you to finish reading this article.

Welcome to the Try Ubuntu project.  Yes, we really are footing the bill for this, it won’t cost you a dime.  Click that large inviting “Let’s go to the cloud” button to start your adventure in cloud computing.

You will need to sign into Ubuntu SSO, because while we’re perfectly happy to pay for one hour, we do need to prevent abuse.  Requiring a login lets us limit this to once per user.  If you don’t have an SSO account you can create one now, and really if you’re going to spend any time at all with Ubuntu or the community, you’re going to want one soon or later anyway.

Ubuntu SSO lets you decide what detailed information to send back to any requesting web service.  You don’t have to send any of these details back to use this service, but if you have a Launchpad profile and uploaded SSH keys, you’ll get a better experience if you send at least your username.

Next you’ll get to choose what you want running on your trial instance.  You’ll also have to agree not to be abusive with your instance, do anything illegal, or generally cause other people problems as a result of our generosity.  Seriously, just don’t do it.

Currently we offer a base Ubuntu Server, running just the default installation, as well as the base server plus WordPress, Drupal or MoinMoin.  These aren’t pre-made images, they’ll be installed after the new server is provisioned, just like you would do manually, only thanks to cloud-init, we have it all automated.  You will use the same Ubuntu Server AMI regardless of which service you choose.

That’s all you need to do!  Now click the “Launch” button and the website will ask for a new m1.small instance through Amazon’s EC2 API.  This API is available to anyone, by the way, so you can script your own cloud deployments in exactly the same way.  We are using the boto python library to access the API from within Django.

After we get an instance reserved, we have to wait for Amazon to start it.  It only takes about a minute for Amazon to start up your new instance. In the mean time, this page will periodically refresh itself, and will let you know once your instance has started.  Did you notice that “View Cloud-Config” link?

This is what will be run on your new instance as soon as it’s ready.  You can copy this script, and use it later to start up your own permanent Ubuntu Server instances on AWS or any other EC2 compatible cloud host.  It is this script that will install Apache, MySQL and WordPress, configure them all properly, and get them all running.

Now your instance has started, and so has your countdown clock. At this point Ubuntu is fully booted, and your cloud-init script is busy installing all the packages and dependencies needed to run WordPress, all you have to do is sit back and wait.  Deploying is hard work huh?.  Don’t worry, I won’t tell your boss.

Hope you didn’t get too comfy, because 3 minutes later and we’re done!  Here you’ve given the command to SSH into your new instance.  If you’re running this from Windows, you’ll need to get something like PuTTY, because last time I checked Redmond still thinks that unencrypted Telnet is a good idea.  Trust me, you want SSH.

So fire up a terminal (yes, a terminal, you’re in server-land now buddy) and copy/paste the ssh command to get connected to your new instance.

Next you will be prompted for your password, it’s back on the web page, just copy/paste it into the terminal.  Remember how I said earlier that having a Launchpad profile with uploaded SSH keys would give a better experience?  Well if you’ve got all that, then you won’t see this step.  You see, part of the initialization that happened when your new instance started was to download your public SSH keys from Launchpad, allowing you to use your private SSH keys for authentication.  Nice huh?

So now you’re connected to your new instance.  But what’s all that stuff at the bottom of your terminal now?  That my friend, is Byobu, Ubuntu’s highly customized profile for GNU Screen.   Describing all of it’s wonderful goodness would take another full blog post, so I’ll just point out the highlights.  F2 creates a new “tab”, and you can switch between them with F3/F4.  Down at the bottom are some system monitoring widgets for things like load and memory usage.  In green characters is a special widget for Amazon EC2 that gives you an estimate of how much your instance is costing you (a whopping $0.09 USD at this point), and in blue characters is a clock showing how long your trial has been running (so you’ll know how much of your 55 minutes you have used).

Alright, your instance is running and configured, now what?  Well, did you  see that link on the web page?

That one, next to “Try going to”.  Click on it.

Well look at that, it’s your new WordPress site, just waiting for you to give it a name (and also username and password).  No unzipping, copying, apache configs, database setup, nothing.  We literally can’t make it any easier.

So pick a name, give it a password and email, and you’re all set!  Yes, I know my password was weak.  Actually my password was ‘password’.  Hey, it was only up for 55 minutes, I’m not going to spend extra time thinking of a secure password.  Come on, we’ve got a WordPress site to play with!

 

There you are, your new WordPress site is deployed.  And how are we doing on time?  Well if you hadn’t spent so much time reading along with this article, you’d have at least 45 minutes left out of the original 55.  Heck, I wasted a bunch of time taking screenshots along the way, and I still had more than 40 remaining.  How far do you think your sysadmin would have gotten in this same amount of time?  He probably just got back form refilling his coffee (which, to his credit, really is necessary before attempting a deployment the old fashioned way).

After a while, as you get close to the end of your trial period, you’ll get these helpful messages in your terminal session, letting you know how much longer you have.  And before you start thinking that you can use your mighty sudo powers to stop your instance’s termination, sorry pal, but we keep track of them on our end too, and your instance will be killed through the same EC2 API that launched it.  But I sure hope you had fun.

 

So now you’ve seen how fast and easy it is to deploy not just Ubuntu Server in the cloud, but actual, useful services running on top of it.  We offer you three popular software packages for websites, but those are only the tip of the iceberg.  You can write cloud-init scripts for anything you want to deploy on Ubuntu, even your own in-house build applications.  Then you too can deploy into the cloud with the click of a button.

What’s that you say?  You don’t have a handy dandy webapp for one-click deployments into the cloud?  Oh but you do!  You see, everything you just saw is open source, you can download it from lp:awstrial on Launchpad.  Use it to run your own trials, or just to learn how we did it so you can write your own internal provisioning service.  It defaults to using Amazon’s EC2 cloud, but you can point it to any EC2 compatible cloud.  We ran it internally against an OpenStack EC2 cloud during development and testing.

Did you enjoy your trial?  Leave us some feedback on what you liked, what you didn’t, and what you want us to offer in the future.

Read more