Freitag, 21. September 2012

Web: Placeholder.it

I found this very useful site on the internet a few days ago. Placeholder.it provides a very simple service. You can request an image from them specifying a width and height and they will provide a grey image at your requested size.

If you are lazy like me, you can use these images to quickly build spacers or blind images to layout your site.

This will generate an image with 350x150 pixels.

http://www.placehold.it/350x150

Mittwoch, 22. August 2012

C++: String startsWith function

This function checks if the supplied string starts with the specified prefix.

bool string_startsWith(const std::string &str, const std::string &prefix) {
  return std::equal(prefix.begin(), prefix.end(), str.begin());
}

Freitag, 17. August 2012

ActionScript: Distance between line and a point

/**
 * Computes the difference vector between two vectors.
 * @param p1 First vector (from (0,0).
 * @param p2 Second vector (from (0,0).
 * @return  The difference vector between p1 and p2.
 */
public static function differenceVector(p1 : Point, p2 : Point) : Point {
 return new Point(p2.x - p1.x, p2.y - p1.y);
}
  
/**
 * Projects a point onto a line. Returns the projected point or null if
 * the projection does not fall within the segment. 
 * 
 * @param lineStart First point of the line.
 * @param lineEnd Second point of the line.
 * @param p Point that should be projected onto the line given by 
 *              lineStart and lineEnd.
 * @return      The projected point on the line or null if the projection line
 *              falls not between lineStart and lineEnd.
 */
public static function projectPointOntoLine(lineStart : Point, 
                                            lineEnd : Point, 
                                            p : Point) : Point {
 // Difference vector is the line.
 var l : Point = differenceVector(lineEnd, lineStart);
   
 // Determine determinant and divide it by the length of the line.
 var u : Number = (((p.x - lineStart.x) * (lineEnd.x - lineStart.x)) + 
                          ((p.y - lineStart.y) * (lineEnd.y - lineStart.y))) / 
                         (l.length * l.length);
   
 // Check whether the projection lies between lineStart and lineEnd.
 if ((u < 0) || (u > 1)) {
  return null;
 }
   
 // Compute projection point.
 var pp : Point = new Point(lineStart.x + u * (lineEnd.x - lineStart.x),
                                   lineStart.y + u * (lineEnd.y - lineStart.y));
   
 return pp;
}
  
/**
 * Computes the shortest distance between a line and a point. If the 
 * point cannot be projected onto the line segment specified by
 * lineStart and lineEnd, the function will return -1.
 * 
 * @param lineStart First point of the line.
 * @param lineEnd Second point of the line.
 * @param p Point that should be projected onto the line given by
 *              lineStart and lineEnd.
 * @return      The distance between p and the line or -1 if p cannot be
 *              projected onto the line.
 */
public static function distanceLinePoint(lineStart : Point, 
                                         lineEnd : Point, 
                                         p : Point) : Number {
 var projection : Point = projectPointOntoLine(lineStart, lineEnd, p);
 if (projection != null) {
  return differenceVector(p, projection).length;
 } else {
  return -1;
 }
}

Donnerstag, 16. August 2012

FlashDevelop: CamelCase cursor hopping

As a keyboard person I really like to navigate text fast. I use CRTL-Left and CRTL-Right in any text editor to jump between words. Eclipse added another feature onto the word hopping. It recognizes the CamelCase nature of code. So if I have

myLayoutManager

it will position the cursor at the beginning of my on the first press, on the L of Layout on the second press and on the M of Manager on the thrid press, before jumping to the next word or character. For editing code this is simply great. Saves so much time and single curor movement.

Because I have not found an official name for this behavior I will call it CamelCase cursor hopping (CCCH).

FlashDevelop does not support CCCH, but you can install plugins. Mørkeulv wrote the Wordshifter plugin that enables it in FlashDevelop. Just download the binary from his blog and install it. He even provides the source code for the plugin.

You can customize the jumping behavior by providing your own regular expression and which keys should be used to hop around the code. This way you can reenable the old word jumping and have CCCH along each other.

Thanks for the addon!

Links:



Mittwoch, 15. August 2012

Android: Install USB drivers for HP Touchpad

If you have a HP Touchpad running CyanogenMod Android, you can use it to develop and test applications on the native device. But before you can use the tools from the Android SDK on a Windows PC, you have to install the USB drivers.

The Android SDK provides USB drivers for Windows in <sdk-directory>/extras/google/usb-drivers. But those drivers don't include the needed information to identifiy a CyanogenMod Android. CyanogenMod has a patched android_winusb.inf file and the instructions on how to install it available in their wiki.

The patched driver information file does not contain information for the HP Touchpad. You have to patch the patched INF file again for the Touchpad.

So here are the steps needed to connect the Touchpad to a Windows PC to start developing/debugging:


1. Turn on MTP, which is disabled by default, on the Touchpad.
Go to Settings -> Storage -> click on the three small squares in the upper right corner -> USB computer connection -> enable Media Device (MTP)
2. Add HP Touchpad Vendor and Product IDs to the INF file
Edit the android_winusb.inf either from the CyanodgenMod wiki or the original file from the Android SDK. Add these lines at the end of the sections for [Google.NTx86] and [Google.NTamd64].

; HP TouchPad
%SingleAdbInterface%    = USB_Install, USB\VID_0BB4&PID_6860&REV_0227&MI_01
%CompositeAdbInterface% = USB_Install, USB\VID_0BB4&PID_6860&MI_01


3. Update driver in Windows Device Manager
Open the Device Manager in Windows, selection the unknown USB device and update drivers. Select the android_winusb.inf file you just modified.

Dienstag, 14. August 2012

FlashDevelop: Curly braces and CodeFormatter

I have been using FlashDevelop lately. It is a great IDE for ActionScript development. But coming from an Eclipse background I had to relearn a lot of keyboard shortcuts. One feature I really like in an IDE is using a code formatter. FlashDevelop contains such a feature and it can be accessed by right clicking in the editor and selecting Refactor->Code Formatter or by using the keyboard shortcut CRTL-Shift-2.

But as always every programmer hates it when the automatic code formatter destroys lovingly crafted indentation. The default behavior of the FlashDevelop formatter is to put the curly braces on a new line. I have struggled a long time with myself over whether to put them on a new line or keep them on the same line. The later option won. And now it just feels awkward to see them on a line of their own.

Of course FlashDevelop provides a solution - a little bit hidden. Go to

Tools -> Program Settings -> CodeFormatter -> BraceStyle

and change it to "OnLine".

Mittwoch, 4. Juli 2012

Windows: Create/Delete a service

Create a new service:
sc create SERVICENAME binpath= "<path to executable> -arg1 -arg2"

The excutable is installed as a service. See my other post about building your own service. Please note that there is a space after binpath=.DIf the service needs arguments (which will be passed to the ServiceMain function), you can add them (arg1, arg2 above). Just be sure to surround the executable and the arguments with quotes.

Delete an existing service:
sc delete SERVICENAME

The service will be deleted after the next restart.

Dienstag, 3. Juli 2012

C++: Create a Windows service

Creating a Windows service is fairly simple. You start by writing you main() function as you would normally. In the main() function you have to call StartServiceCrtlDispatcher(). The programm has to make this call at least 30 seconds after it has launched. Otherwise Windows will assume the service failed to start up. So the simplest main() function for a service will look something like this:

int main(int argc, char **argv) {
  SERVICE_TABLE_ENTRY serviceTable[2];
  serviceTable[0].lpServiceName = "ServiceTest";
  serviceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION) ServiceMain;

  serviceTable[1].lpServiceName = NULL;
  serviceTable[1].lpServiceProc = NULL;
  StartServiceCtrlDispatcher(serviceTable);  
}

StartServiceCrtlDispatcher() takes an array of SERVICE_TABLE_ENTRYs. Each defines the name and callback function of one service it should start. The callback function is run in its own thread and StartServiceCrtlDispatcher() will only return after all services it has started have been stopped.

As the name indicates, the callback function of the service is the real main function of the service. Here the service should do whatever it is supposed to do. In order to by managed by the Services interface of Windows the service has to implement a callback to a control handler with the RegisterServiceCrtlHandler() function. The callback is invoked every time the service should change its state (Stop for example).

The service should use the SetServiceStatus() function to report its current status to the system. The following code can be used as a skeleton for your own service.

SERVICE_STATUS ServiceStatus; 
SERVICE_STATUS_HANDLE hServiceStatus; 

// Control handler function callback
void ControlHandler(DWORD request) 
{ 
  switch(request) { 
    case SERVICE_CONTROL_STOP: 
      // Service has been stopped
      ServiceStatus.dwWin32ExitCode = 0; 
      ServiceStatus.dwCurrentState  = SERVICE_STOPPED; 
      SetServiceStatus (hServiceStatus, &ServiceStatus);
      return; 
 
    case SERVICE_CONTROL_SHUTDOWN: 
      ServiceStatus.dwWin32ExitCode = 0; 
      ServiceStatus.dwCurrentState  = SERVICE_STOPPED; 
      SetServiceStatus (hServiceStatus, &ServiceStatus);
      return; 
        
    default:
      break;
  } 
 
  // Report current status
  SetServiceStatus (ServiceStatusHandle,  &ServiceStatus);
} 

// Service main function callback
void ServiceMain(int argc, char **argv) {
  int count = 0;
  int result;
 
  ServiceStatus.dwServiceType = SERVICE_WIN32; 
  ServiceStatus.dwCurrentState = SERVICE_START_PENDING; 
  ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | 
                                       SERVICE_ACCEPT_SHUTDOWN;
  ServiceStatus.dwWin32ExitCode = 0; 
  ServiceStatus.dwServiceSpecificExitCode = 0; 
  ServiceStatus.dwCheckPoint = 0; 
  ServiceStatus.dwWaitHint = 0; 

  hServiceStatus= RegisterServiceCtrlHandler(
                    "ServiceTest", 
                    (LPHANDLER_FUNCTION) ControlHandler); 
  if (hServiceStatus == (SERVICE_STATUS_HANDLE) 0) { 
    // Registering Control Handler failed
    return; 
  }  

  ServiceStatus.dwCurrentState = SERVICE_RUNNING; 
  SetServiceStatus (hServiceStatus, &ServiceStatus);

  while (ServiceStatus.dwCurrentState == SERVICE_RUNNING) {
    // Do you important service stuff here
    Sleep(5000);
  }
}

This service will run as long as its state is SERVICE_RUNNING. Once the state changes the ServiceMain function will return and the service will terminate.

Visit these links for more examples and explanations:



Donnerstag, 26. April 2012

Shell: Determine lines of code

Use this command to determine the number of lines of code you have written for your C++ project.

find ../src -name "*.cpp" -or -name "*.h" | xargs wc -l

Mittwoch, 14. März 2012

Windows: Shutdown via command line

You can shutdown a computer running Windows using the command line. Use the shutdown command to force a shutdown, reboot or hibernate.

Shutdown computer
shutdown /s

Restart computer
shutdown /r

Hibernate computer
shutdown /h

The shutdown command is available since Windows XP. On older versions of Windows use this command:

Restarting the computer
RUNDLL.EXE user.exe,exitwindowsexec

Shut down the computer
RUNDLL32.EXE user,exitwindows

Freitag, 2. März 2012

Java: Creating an undecorated window with LWJGL

This system property removes the window decorations (title bar, icons, border) from LWJGL applications in window mode.

org.lwjgl.opengl.Window.undecorated=<true|false>

You can specify the property on the command line, when you start the application

java -Dorg.lwjgl.opengl.Window.undecorated=true -jar application.jar

or in your code by setting it manually.

System.setProperty(" org.lwjgl.opengl.Window.undecorated", "true");

See the LWJGL wiki for more hidden property switches.

Donnerstag, 23. Februar 2012

C++: string tokenzier

This function splits a string into substrings. It uses a list of delimiter characters to find the boundaries of the substrings.

void tokenize(const std::string &str, 
              std::vector<std::string> &tokens, 
              const std::string &delimiters = " ") {
  string::size_type lastPos = str.find_first_not_of(delimiters, 0);
  string::size_type pos = str.find_first_of(delimiters, lastPos);

  while (std::string::npos != pos || std::string::npos != lastPos) {
    tokens.push_back(str.substr(lastPos, pos - lastPos));
    lastPos = str.find_first_not_of(delimiters, pos);
    pos = str.find_first_of(delimiters, lastPos);
  }
}

Usage:
vector<std::string> tokens;
tokenize("I want to tokenize this!", tokens);

tokenize("2012-02-20", tokens, "-");

Mittwoch, 22. Februar 2012

Tools: Installing WebDAV support in TotalCommander 64 bit

After I switched to the 64 bit beta version of Total Commander, I noticed that I could no longer connect to my WebDAV server. The problem was the WebDAV plugin for Total Commander. It was a 32 bit version and could not be loaded into the 64 bit Commander.
The plugins page still has the 32 bit plugin for download. I think this is ok, because the 64 bit version of Total Commander is still in beta. A 64 bit plugin has already been written. You have to go to the forums and dig around a little bit to find the link.
After installling the beta plugin everything worked fine again.

Download WebDAV 64 bit plugin for Total Commander

Sonntag, 12. Februar 2012

Java Slick game development - 64 bit problems

When setting up my project and writing the simple basic application Slick crashed on me with an error:

java.lang.UnsatisfiedLinkError: Drones\lib\lwjgl.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform

I'm running Windows 7 on a 64 bit system and I have the 64 bit Java VM installed. You will not see this error if you are running the 32 bit version of Java on your 64 bit system.

To solve this problem I had to download the current version of LWJGL from their site and copy the windows DLLs and the lwjgl.jar that is included in the download into my project lib directory. This will replace the lwjgl.jar that came with the Slick distribution.

So now my project looks like this in the project explorer:

Samstag, 11. Februar 2012

Java: Slick game development - Part #2 The first program

After setting up the project in my last post, we now create a very simple sample application that we will extent in the future.

The following code creates a new Slick game. It is not much yet - just a black window. But this window is already an OpenGL frame.

package codebrocken.drones;

import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.SlickException;

public class Drones extends BasicGame {

 public Drones(String title) {
  super(title);
 }

 @Override
 public void render(GameContainer arg0, Graphics arg1) throws SlickException {
  // TODO Auto-generated method stub

 }

 @Override
 public void init(GameContainer arg0) throws SlickException {
  // TODO Auto-generated method stub

 }

 @Override
 public void update(GameContainer arg0, int arg1) throws SlickException {
  // TODO Auto-generated method stub

 }

 public static void main(String[] args) {
  try {
   AppGameContainer appContainer = new AppGameContainer(new Drones("Drones"));
   appContainer.start();
  } catch (SlickException e) {
   e.printStackTrace();
  }
 }
}

The class itself is derived from BasicGame, which does a lot of work for us. We just have to implement a few methods that will do our actual work later. The main method creates a new AppGameContainer and initializes it with our game.

Freitag, 10. Februar 2012

Java: Slick game development - Part #1 Setting up Eclipse

Slick is a 2D game library for Java. It uses OpenGL for rendering. It also contains a lot of utility functions. I want to do a little bit of game programming so instead of writing my own engine (as I would do normally), this time I'm testing Slick.

Step #1: Download Slick libraries

You can get the Slick libraries here. Just download the complete package. It includes the LWJGL libraries that provide the OpenGL layer.

Step #2: Create a new Eclipse project

Create a new Java project in Eclipse and amed it Drones.Nothing fancy at this point.


Step #3: Import the libraries

I created a new lib folder, that will contain all my libraries. I copied the slick.jar and lwjgl.jar from the Slick archive into that folder. To add them to the build you have to open the properties of the project and select "Build Path". Then click "Add JARs..." and select the two JAR archives from the lib folder.


Step #4: Import the native libraries

The LWJGL library needs a native runtime library. On windows systems this is the lwjgl.dll you can find in the root folder of the Slick archive. I copied the DLL into the lib folder too. You can choose a "native" subfolder as you like.
You now have to open the "Referenced Libraries" branch in the package explorer and right click the lwjgl.jar to open its properties. In the properties dialog select "Native Library". Here you can select the "Location path" where Eclipse will look for native libraries, when it starts the application. Just select our lib directory here.
Under the hood this step sets the java.library.path property to point to the lib directory. This property defines where Java searches for native libraries.


Now you are done with setting up Eclipse. You should have a structure that looks something like this:






Freitag, 3. Februar 2012

Tools: Total Commander and TortoiseSVN

I'm a Total Commander fanatic. It is the program, that always run when I'm working on my computer. Even when I'm not working. But recently I had a problem with TortoiseSVN and Total Commander. When you open a SVN folder in Windows Explorer Tortoise will overlay the icon according to the modification state of the contents. This way you can see if there are some modification in your folder and you need to commit them. But Total Commander would not show those icons and the context menu would not contain the SVN commands.

This problem is caused by my new machine being a 64 bit system. As this article details the 32 bit version of Total Commander cannot access the functions of the 64 bit TortoiseSVN. There are two possible solutions:

  • Install both 32 and 64 bit versions of Tortoise. That should work, but I find that very ugly.
  • Install the new 64 bit beta version of Total Commander. Yes it is beta, but it works.
I did the second options and am now very happy with a state of the art Total Commander and SVN support. Windows Explorer can again Rest In Peace on my computer!

Donnerstag, 2. Februar 2012

C++: String trim function

This function removes leading and trailing characters from a string. The default character is a space, but you can use any combination of characters, that should be trimmed.

std::string trim(std::string &str, const std::string &trimChars = " ")
{
  std::string result = str.erase(str.find_last_not_of(trimChars) + 1);
  return result.erase(0, result.find_first_not_of(trimChars));
}

Usage:
string s = trim("Hello World!    ");
std::cout << s;
// prints: Hello World!

s = trim("xxx Hello World! xxx", "x");
std::cout << s;
// prints: Hello World!

Mittwoch, 1. Februar 2012

webOS: Detecting Enyo on phones

With the trojan horse maps update for all webOS devices, every device has the option of running Enyo applications. Which is great, but how can you determine whether the user has updated his phone or not?

Arthur Thornton posted a very nice solution in the webOS developer forums.

In the likely situation that no update to older devices is pushed out including enyo, here is a solution...it isn't pretty, but it will get the job done.

The very first thing you need to do is mention in your app description that your app requires "updated system software" included in the Maps app. This way, the users should know to expect this as a requirement, and even though many users don't read the description (grr...) you're still covered by mentioning this.

Then use the following code in your app:

index.html
Code:
<!doctype html>
<html>
  <head>
    <title></title>
    <meta name="viewport" content="height=device-height" />
    <script src="C:\Program Files (x86)\HP webOS\SDK\share\framework\enyo\1.0\framework\enyo.js" type="text/javascript"></script>
  </head>
  <body>
    <script type="text/javascript">
      if (typeof enyo == "undefined")
        window.location = "./requiresEnyo.html";
      else
        new MyApp().renderInto(document.body);
    </script>
  </body>
</html>

requiresEnyo.html
Code:
<!doctype html>
<html>
  <head>
    <title>Install Maps and Enyo</title>
    <meta name="viewport" content="height=device-height" />
    <script src="/usr/palm/frameworks/mojo/mojo.js" type="text/javascript" x-mojo-version="1" />
  </head>
  <body>
    <div style="padding: 5px">
      Attention: This application requires an updated version of the system software which is not available on your phone. To install this updated software, you must install the updated Maps app by tapping the below button (will open the App Catalog):
    </div>
    <div class="palm-button" onclick="installEnyo()">Install</div>
    <script>
      window.onload = function() {
        PalmSystem.stageReady(); // required for page to load
      };
      function installEnyo() {
        window.location = "http://developer.palm.com/appredirect/?packageid=com.palm.app.maps";
      }
    </script>
  </body>
</html>

Note that this loads Mojo (which will obviously be on the device) in a separate HTML file *if* enyo isn't already on the device (i.e. a Pre2). This was tested to work on my Pre2.

Also note that to put enyo in your app, your users must be using webOS 1.4.5 or newer because the maps app has a minimum webOS version of 1.4.5 (and thus, users prior to 1.4.5 won't see it in the Catalog and won't be able to update) due to (I'm assuming) using the version 2.0 packaging format.

If you have your app currently deployed to users running webOS versions prior to 1.4.5, you will have to retain a Mojo version in it (because the app can still be updated even if you change minimum OS version to 1.4.5 unless changes have recently gone into effect to fix that; this doesn't cause issues for the Maps app because technically, that app is not update-capable on devices prior to the Pre3 without the workaround they came up with).

Arthur Thornton
webOS Application Engineer
Appstuh

Dienstag, 31. Januar 2012

webOS: Kind inheritance tree for Enyo 1.0

With Enyo 2.0 available this information might not be as useful as it was before. Here you can find a PDF document with a tree inheritance model of all Enyo kinds. I always like a visual representation and you can find a new kind you haven't used before.

Montag, 30. Januar 2012

webOS: Implementing promises with Enyo components

I recently needed to make a few web service calls from within my Enyo application. I wanted to use the cujojs.com when.js library for synchronization with promises. Promises in itself are a very intriguing concept and I will write another blog post about them in the future.

In essence a promise works this way:

webservice.login(username, password).then(
  function loginSuccess(response) {
    enyo.log("Login successful");
  }, function loginFailed(response) {
    enyo.log("Login failed.");
  }); 

The login function of the web service sends out an request asynchronously and returns immediately. The usual way to handle this kind of behavior is to pass a set of callback functions to the login method, that are called when the service is successful or had failed. Promises allow for more compact code. See this article for a good explanation.

To make a web service call in Enyo you normally use an enyo.WebService component like this:

enyo.kind({
  name : "LoginComponent",
  kind : enyo.Component,
  components : [ {
    kind : "WebService",
    name : "loginWebService",
    url : "https://<your service URL here>",
    method : "post",
    onSuccess : "loginSuccess",
    onFailure : "loginFailure"
  } ],

  loginSuccess : function(inSender, inResponse, inRequest) {
    enyo.log("Login successful.");
  },

  loginFailure : function(inSender, inResponse, inRequest) {
    enyo.log("Login failed.");
  }
});

The WebService kind has to event methods onSuccess and onFailure. But in the definition of the kind, those two events take only strings. When the doSuccess or doFailure methods are called the intelligence of the enyo.Object handle the translation from the string to the actual method call.

To implement the promise concept with the WebService kind, we have to create our own kind.

enyo.kind({
  name : "LoginWebservice",
  kind : enyo.Component,
  
  deferred : undefined,
  
  components : [ {
    kind : "WebService",
    name : "loginWebService",
    url : "https://<your service URL here>",
    method : "post",
    onSuccess : "loginSuccess",
    onFailure : "loginFailure"
  } ],

  login : function(inUsername, inPassword) {
    this.deferred = when.defer();
    var params = {
      Username : inUsername,
      Password : inPassword
    };
    this.$.loginWebService.call(params);
    return this.deferred.promise;
  },

  loginSuccess : function(inSender, inResponse, inRequest) {
    this.deferred.resolve(inResponse);
  },

  loginFailure : function(inSender, inResponse, inRequest) {
    this.deferred.reject(inResponse);
  }
});

The component creates a deferred object and resolves/rejects it based upon the result of the web service call. Use the following code to integrate the kind in your own software:

enyo.kind({
  name : "LoginWebServiceTest",
  kind : "Component",
  components : [ {
    kind : "LoginWebservice",
    name: "wsLogin"
  }, {
    kind : "Button", 
    onclick: "loginTest"
  } ],

  loginTest : function(inSender) {
    this.$.wsLogin.login("Username", "MySecretPassword").then(
      function loginSuccess(response) {
        enyo.log("Login successful");
      }, function loginFailed(response) {
        enyo.log("Login failed.");
      }
    ); 
  }
});

x

Montag, 23. Januar 2012

webOS: Login dialog with Enyo

The Enyo framework is very modular and thus allows components to be reused very easy. Here we have a simple modal dialog that can be used to let the user enter a login name and a password.

The dialog will look like this:


Just create a file LoginDialog.js and add it to your enyo.depends() call.

enyo.kind({
  kind : "ModalDialog",
  name : "FFComputing.LoginDialog",
  caption : "Login",
  events: {
    onOK: "",
    onCancel: ""
  },
  components : [ {
    kind : "Group",
    name : "LoginCaption",
    caption : "Login",
    components : [ {
      kind : "Input",
      name : "loginInput",
      hint : "Login...",
      autoWordComplete : false,
      spellCheck : false,
      autocorrect : false
    } ]
  }, {
    kind : "Group",
    name : "PasswordCaption",
    caption : "Password",
    components : [ {
      kind : "PasswordInput",
      name : "passwordInput",
      hint : "Password...",
      spellCheck : false,
      autocorrect : false
    } ]
  }, {
    layoutKind : "HFlexLayout",
    components : [ {
      kind : "Button",
      flex : 1,
      caption : "OK",
      onclick : "okClicked",
      className : "enyo-button-blue"
    }, {
      kind : "Button",
      flex : 1,
      caption : "Cancel",
      onclick : "cancelClicked"
    }]
  }, ],
 
  okClicked: function(sender) {
    this.close();
    this.doOK(this.$.loginInput.getValue(), this.$.passwordInput.getValue());
  },
 
  cancelClicked: function(sender) {
    this.close();
    this.doCancel();
  }
});

Samstag, 21. Januar 2012

webOS: Open a socket

If you use Enyo or Mojo for your webOS application development, you cannot connect to a server that runs anything else than a http protocol. This limitation comes from webOS programs effectively running inside a web browser. Therefore they can make only http requests using XmlHttpRequest calls.

But webOS Version 2.X introduced support for node.js. Programs can use this library to open a socket to any server using any protocol. The downside is, that you have to do a little bit of work to get this thing moving: you have to write your own javascript service.

This code opens a connection to port 28820 on host 172.20.1.65 and sends the string "HELLO" with a line break. When it receives an response, it is written to the console.

// import node.js net library
var net = IMPORTS.require('net');

// constructor for service assistant
var socketAssistant = function() {
};

// function is called by framework to execute the service
socketAssistant.prototype.run = function(future) {
    // future contains the result of the function call
    // use it to transfer information to your program
    future.result = {
        reply : "Hello " + this.controller.args.name + "!"
    };

    // check if the node.js net library has been loaded
    if (!net) {
        console.error("net == NULL");
    }

    // create a new client connection
    var client = net.createConnection(28820, '172.20.1.65');

    // event handler when data arrives from the server
    client.on("data", function(data) {
        console.error("Received data: " + data.toString());
    });

    // event handler called after a connection has been established
    client.on("connect", function() {
        console.error("Connection established !");
        client.write("HELLO\n"));
    });

    // event handler called in case of an error
    client.on('end', function() {
        console.error("Connection killed.");
    }); 
};

The code is pretty simple but does not contain the framework needed to integrate it into a service. That is fodder for another post soon. Meanwhile take a look at the SDK documentation for writing javascript services.

Older versions of webOS only support node.js 0.2.3. Version 3.0.4 of webOS made an update to node.js 0.4.12 (see release notes). Be aware of that fact when using the node.js functions.

See the node.js (version 0.2.3) documentation for what you can do with the connection.

Freitag, 20. Januar 2012

ANT: Replace string tokens

You can use ANT to copy files during the build process. In addition to that you can modify these files on the fly and change their content. This allows you to replace tokens in the original file.

I used this mechanism in my post yesterday to generate a configuration file for my webOS application. The original file contained the structure and a few tokens, such as the vendor name or application id. During the build the original file is copied into the source folder with the replaced tokens. Very convenient!

This is my package target. It copies the file appinfo.json.in from the base directory of my project into the src directory and replaces the tokens
  • id
  • version
  • title
  • vendor
with the values from the properties defined in the build file. After the copy operation the target executes the package command from the webOS SDK.

<property name="appinfo.id" value="codebrocken.test" />
  <property name="appinfo.version" value="1.0.0" />
  <property name="appinfo.title" value="TestProject" />
  <property name="appinfo.vendor" value="CodeBrocken" />

  <!-- ================================= 

    target: package              

    ================================= -->

  <target name="package" depends="depends" description="Package the application.">
    <copy file="appinfo.json.in" tofile="src/appinfo.json">
      <filterchain>
        <replacetokens>
          <token key="id" value="${appinfo.id}" />
          <token key="version" value="${appinfo.version}" />
          <token key="title" value="${appinfo.title}" />
          <token key="vendor" value="${appinfo.vendor}" />
        </replacetokens>
      </filterchain>
    </copy>

    <exec executable="cmd">
      <arg value="/c" />
      <arg value="palm-package" />
      <arg value="src" />
      <arg value="-o" />
      <arg value="bin" />
    </exec>
  </target>

The file appinfo.json.in contains the following JSON string. See the webOS SDK for the description of the fields.

{
  "id": "@id@",
  "version": "@version@",
  "vendor": "@vendor@",
  "type": "web",
  "main": "index.html",
  "title": "@title@",
  "uiRevision": "2",
  "icon": "images/icon.png"
}

The ANT script uses a filter chain to modify the source file, while it is being processed. A filter chain consists of one or more filter readers. One such filter reader is replacetokens. It replaces a token in the form of "@key@" with the specified value.

Donnerstag, 19. Januar 2012

webOS: Using ANT


I like ANT. I like the ANT integration in Eclipse. I like having all the targets of my project visible in a neat tree and just having to click on one to execute them.

Developing webOS applications is done using a few batch scripts that are provided by the SDK. You can find the scripts in the bin folder of your SDK installation directory. Newer versions of the SDK add this directory to your path (at least on Windows, haven't tried it on other platforms for some time), so you can just open a console and type the command to build your application.

So I came up with the idea of an ANT build script for the webOS batch files. Nothing fancy just a few targets, so I don't have to leave Eclipse in order to build my application.

<?xml version="1.0" encoding="UTF-8"?>
<project name="TestProject" default="all">
  <description>
    ANT project for HP Touchpad development
  </description>
  <property name="appinfo.id" value="codebrocken.test" />
  <property name="appinfo.version" value="1.0.0" />
  <property name="appinfo.title" value="TestProject" />
  <property name="appinfo.vendor" value="CodeBrocken" />

  <!-- ================================= 

    target: all              

    ================================= -->

  <target name="all" depends="depends" description="Build the project.">
  </target>

  <!-- - - - - - - - - - - - - - - - - - 

    target: depends                      

    - - - - - - - - - - - - - - - - - -->

  <target name="depends">
  </target>

  <!-- ================================= 

    target: package              

    ================================= -->

  <target name="package" depends="depends" description="Package the application.">
    <copy file="appinfo.json.in" tofile="src/appinfo.json">
      <filterchain>
        <replacetokens>
          <token key="id" value="${appinfo.id}" />
          <token key="version" value="${appinfo.version}" />
          <token key="title" value="${appinfo.title}" />
          <token key="vendor" value="${appinfo.vendor}" />
        </replacetokens>
      </filterchain>
    </copy>

    <exec executable="cmd">
      <arg value="/c" />
      <arg value="palm-package" />
      <arg value="src" />
      <arg value="-o" />
      <arg value="bin" />
    </exec>
  </target>

  <!-- ================================= 

    target: deploy              

    ================================= -->

  <target name="deploy" depends="package" description="Deploy to device.">
    <exec executable="cmd">
      <arg value="/c" />
      <arg value="palm-install" />
      <arg value="bin/${appinfo.id}_${appinfo.version}_all.ipk" />
    </exec>
  </target>

  <!-- ================================= 

    target: launch              

    ================================= -->

  <target name="launch" depends="deploy" description="Launch the application on the device.">
    <exec executable="cmd">
      <arg value="/c" />
      <arg value="palm-launch" />
      <arg value="${appinfo.id}" />
    </exec>
  </target>

  <!-- ================================= 
 
    target: log              

    ================================= -->

  <target name="log" depends="depends" description="Start logging for the application on the device.">
    <exec executable="cmd">
      <arg value="/c" />
      <arg value="palm-log" />
      <arg value="-f" />
      <arg value="${appinfo.id}" />
    </exec>
  </target>

  <!-- ================================= 

    target: emulator              

    ================================= -->

  <target name="emulator" depends="depends" description="Start the emulator">
    <exec executable="cmd">
      <arg value="/c" />
      <arg value="palm-emulator" />
    </exec>
  </target>
</project>

The interesting targets of the build file are:
  • emulator - start the emulator
  • log - start live logging for your application on the device
  • launch - package, install and launch your application on the device
The targets package and deploy are used by launch to create and install the application on the device.

The package target uses string token replacement to modify the template for my appinfo.json file. This way I do not have to touch the file and can reuse it in my next application. See my post about the string token replacement.