The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

App::XUL - Perl extension for creating deployable platform-independent standalone desktop applications based on XUL and XULRunner.

WARNING: PRE-ALPHA - DON'T USE FOR PRODUCTION!

SYNOPSIS

  use App::XUL;
  my $app = App::XUL->new(name => 'MyApp');
  
  $app->add(
    Window(id => 'main',
      Div(id => 'container', 'style' => 'background:black', 
        Button(label => 'click', oncommand => sub {
          ID('container')->style('background:red');
        }),
      ),
    ),
  );
  
  $app->bundle(path => '/path/to/myapp.app', os => 'macosx');  

XUL (+ XULRunner) makes it easy to create applications based on XML, CSS and JavaScript. App::XUL tries to simplify this even more by exchanging XML and JavaScript with Perl and providing an easy way to create deployable applications for the platforms XULRunner exists for.

WHY XUL/XULRUNNER

XUL provides a set of powerful user widgets that look good and work as expected. They can be created with minimal effort and their appearance can be manipulated using CSS.

XUL is based on web technologies like XML, CSS and JavaScript. So anyone who is able to use these is able to create cool desktop applications.

Here is the homepage of the XUL and XULRunner projects at Mozilla.

DESCRIPTION

new() - Creating an application

The constructor new() is used to create a new application

  my $app = App::XUL->new(name => 'MyApp');

Options

name => string

The name of the application. Later also used as the application executable's name.

add() - Add a window to an application

add() adds a window to the XUL application. The XML for the window tag and its contained tags is created using Perl functions. The names of the Perl functions used to create the XML tags corresponds to the tagnames, just the first letter is uppercase:

  $app->add(
    Window(id => 'main',
      Div(id => 'container', 'style' => 'background:black', 
        Button(label => 'click', oncommand => sub {
          ID('container')->style('background:red');
        }),
      );
    )
  );

Keep in mind, that add will fail if the added tag is NOT a window tag. In XUL the root is always a window tag.

The first window beeing added is considered the main window and shown on startup.

bundle() - Creating a deployable executable

  $app->bundle(path => '/path/to/myapp.app', os => 'macosx');  

This will create a complete standalone XUL application containing all XML code.

Some general information about XUL application deployment.

Options

path => string

os => string

The systems currently supported are:

chrome (native Chrome application)

This creates the usual directory for a XULRunner based application containing all needed files, except the XULRunner executable itself. A start Perl script for various operation systems is created as well, e.g. you can start the application by executing the start_macosx.pl file for Mac OS X.

Note for Linux users: You have to add the directory containing the xulrunner (or xulrunner-bin) executable to your $PATH variable or adjust the start_linux.pl script accordingly. This is due to the fact that XULRunner for Linux us currently in beta phase and can only be used by unpacking it into a local directory.

macosx (Mac OS X)

Apple Documentation

win (Windows)

coming soon

deb or rpm (Ubuntu Linux)

tbd. Either a *.deb or *.rpm Paket.

debug => 1/0

If debug is set to 1, a jsconsole is started together with the application which can be used to debugging messages. The default is debug = 0, so no jsconsole is started.

Creating interface components

Handling events

Events can be handled by attaching an event handler to an interface component. Event handlers can either be written in Perl or in JavaScript.

Here is an example of a Perl event handler that reacts on the mouse click of a button:

  Button(label => 'click', oncommand => sub {
    # access environment and evtl. change it
    # ...
  });

Here is a similar JavaScript event handler:

  Button(id => 'btn', label => 'click', oncommand => <<EOFJS);
    // here is some js code
    $('btn').update('Alrighty!');
  EOFJS

JavaScript event handlers are executed faster than the Perl ones, due to the architecture (see below).

Changing the environment from Perl

This refers to all activities within Perl event handlers that change the DOM of the XUL application. An example is the addition of another window, the insertion or update of a button label or the deletion of a style attribute etc.

Some things are important here:

Changes happen on the server side first and are transferred to the client side (the XUL application) when the event handler terminates.
To manually transfer the latest changes to the client side use the PUSH() function.

Get (XML) element

The first step of changing the DOM is to get an element on which the changes are applied. The ID() function is used for that:

  my $elem = ID('main');

The ID() function only works WHILE the application is running. Any changes to the object returned by the ID() function are transferred immedietly (asynchronous) to the XUL application/client.

Get child (XML) elements

  my $child1 = ID('main')->child(0);
  my $numchildren = ID('main')->numchildren();

Create/insert/append (XML) elements

  my $e = Div(id => 'container', 'style' => 'background:black', 
            Button(label => 'click'));
  ID('main')->insert($e, 'end'); # end|start|...

Edit (XML) element

  ID('container')->style('background:red')->content(Span('Hello!'));

Delete/remove (XML) element

  my $e = ID('container')->remove();

Call event handler on (XML) element

  ID('container')->click();

Register event handler on (XML) element

  ID('container')->oncommand(sub {
    # do stuff here
    # ...
  });

Un-register event handler on (XML) element

  ID('container')->oncommand(undef);
            

EXPORT

None by default.

INTERNALS

This chapter is meant for informational purposes. Sometimes it is nessessary to know how things are implemented to decide, for example, if you should use a Perl or a JavaScript event handler etc.

App::XUL is client-server based. The client is the instance of XULRunner running and the server is a pure Perl based webserver that reacts on the events that are triggered by the XUL interface.

Event handling

Essentially all events are dispatched from XUL as Ajax calls to the Perl webserver which handles the event, makes changes to the DOM etc. The changes are then transferred back to the XUL app where they are applied.

Here is a rough workflow for event handling:

1. Client registers an event (e.g. "mouseover", "click", "idle" etc.)
2. Client sends message to server (incl. parameters and environment)
3. Server calls appropriate Perl event handler subroutine (which may manipulate the environment)
4. Server sends the environment changes to the client as response
5. Client integrates environment changes

Communication protocol

The communication between XUL and server is based on a simple JSON based protocol. The following syntax definition tries to define the protocol. Everything in curly brackets is a JSON object, strings are quoted and non-terminals are written within "<",">" brackets. The pipe symbol ("|") means "OR".

  <CLIENT-REQUEST> := <EVENT>
  
  <SERVER-RESPONSE> := <ACTION>

  <SERVER-REQUEST> := <ACTION>

  <CLIENT-RESPONSE> := <STRING>
  
  <EVENT> := {
    event: <EVENTNAME>,
    id: <ID>
  }

    <EVENTNAME> := 
      "abort" |
      "blur" |
      "change" |
      "click" |
      "dblclick" |
      "dragdrop" |
      "error" |
      "focus" |
      "keydown" |
      "keypress" |
      "keyup" |
      "load" |
      "mousedown" |
      "mousemove" |
      "mouseout" |
      "mouseover" |
      "mouseup" |
      "move" |
      "reset" |
      "resize" |
      "select" |
      "submit" |
      "unload"

  <ACTION> := 
    <UPDATE> | 
    <REMOVE> | 
    <CREATE> | 
    <QUIT> |
    <CHILD> | 
    <NUMCHILDREN> |
    <INSERT> |
    <TRIGGER> |
    <REGISTER> |
    <UNREGISTER> |
    <SETATTR> |
    <GETATTR>
    
    <UPDATE> := {
      action: "update",
      id: <ID>,
      attributes: <ATTRIBUTES>,
      subactions: [ <ACTION>, ... ]
    }

      <ATTRIBUTES> := {<NAME>: <STRING>, ...}
  
    <REMOVE> := {
      action: "remove",
      id: <ID>,
      subactions: [ <ACTION>, ... ]
    }

    <CREATE> := {
      action: "create",
      parent: <ID>,
      attributes: <ATTRIBUTES>,
      content: <STRING>,
      subactions: [ <ACTION>, ... ]
    }
    
    <QUIT> := {
      action: "quit"
    }

    <CHILD> := {
      action: "child",
      id: <ID>,
      number: <NUMBER>
    }
    
    <NUMCHILDREN> := {
      action: "numchildren",
      id: <ID>
    }
    
    <INSERT> := {
      action: "insert",
      id: <ID>,
      position: <POSITION>,
      content: <STRING>
    }
    
    <TRIGGER> := {
      action: "trigger",
      id: <ID>,
      name: <STRING>
    }
    
    <REGISTER> := {
      action: "register",
      id: <ID>,
      name: <STRING>,
      callback: <STRING>
    }
    
    <UNREGISTER> := {
      action: "unregister",
      id: <ID>,
      name: <STRING>
    }
    
    <SETATTR> := {
      action: "setattr",
      id: <ID>,
      name: <STRING>,
      value: <STRING>
    }
    
    <GETATTR> := {
      action: "getattr",
      id: <ID>,
      name: <STRING>
    }

Here are some examples of client requests:

  {event:"click", id:"btn"}

Here are some examples of server responses:

  {action:"update", id:"btn", attributes:{label:"Alrighty!"}}

  {action:"remove", id:"btn"}

  {action:"create", parent:"main", content:"<button .../>"}

Application bundling for Mac OS X

Mac applications are simply directories whose names end with ".app" and have a certain structure and demand certain files to exist.

This is the structure of a XUL application wrapped inside a Mac application as created by App::XUL (files are blue, directories are black):

  MyApp.app/
    Contents/
      Info.plist
      Frameworks/
        XUL.framework/
          The XUL Mac framework
      MacOS
        start.pl (Perl-Script)
      Resources
        application.ini
        MyApp.icns
        chrome/
          chrome.manifest
          content/
            AppXUL.js
            myapp.xul
        defaults/
          preferences/
            prefs.js
        perl/
          server/
            server.pl
          modules/
            All Perl modules the server depends on
        extensions/
        updates/
          0/

The various files have specific functions. When the MyApp.app is clicked, the start.pl program is executed which then starts the server and the client:

Info.plist

Required by Mac OS X. This is the place where certain basic information about the application is read by Mac OS X, before anything else is done. For example, here the start.pl program is defined as the entry point of the application.

start.pl

First program to be executed. Starts server and client.

application.ini

Setups the XUL application. Defines which *.xul files to load, name of application etc.

AppXUL.js

Defines all Javascript functions used by App::XUL to manage the communication with the server.

myapp.xul

Defines the basic UI for the XUL application.

prefs.js

Sets some preferences for the XUL application.

server.pl

This starts the server.

Application bundling for Windows

tbd. Use NSIS or InstallJammer.

Application bundling as DEB package

tbd. See Link.

Application bundling as RPM package

tbd.

ROADMAP

One thing on the todo list is to create a full-duplex connection between client and server so that the client can react on server events directly. This may be implemented using the HTML5 WebSocket protocol. For now all communication is iniciated from the client using AJAX calls.

SEE ALSO

This module actually stands a bit on its own with its approach. XUL modules exist though - XUL::Gui, XUL::Node and a few more.

AUTHOR

Tom Kirchner, <tom@tomkirchner.com>

COPYRIGHT AND LICENSE

Copyright (C) 2011 by Tom Kirchner

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.10.0 or, at your option, any later version of Perl 5 you may have available.