Sunday 15 June 2008

Getting a Tablet PC Touchscreen working under Ubuntu

These are instructions I figured out for a Fujitsu-Siemens Lifebook P1510 but they should be applicable to most tablet PCs. I will note the values to change for different touchscreen resolutions.
This tutorial will also cover getting the touchscreen to map correctly when the screen is rotated for tablet mode.

My screen rotation script uses information found here with additions for correctly killing a previous instance (the touchscreen gets rather crazy when there are multiple copies of the script running for different screen rotations and they are all trying to pull the cursor in different directions).
My touchscreen script is based on one found here but with modifications for rotation.

First you need to install Perl and the required libraries:

sudo apt-get install wacom-tools
sudo apt-get install libx11-dev
sudo apt-get install libxtst-dev
sudo apt-get install x11-common
sudo apt-get install libxtest-dev


download X11::GuiTest, unzip it and install with

perl Makefile.PL
make
make test
make install


Now you need two scripts:
One to kill any previous instance of the touchscreen script, rotate the screen and fire a new instance of the touchscreen script
One to control the touchscreen - a modification of 'tablet5.en.pl' from here

I named my first script 'rotate.sh' and used this code:

#!/bin/bash

#Get the requested rotation angle from the args
angle=$1

#Kill off any previous instance of the touchscreen script
prs=`ps aux | grep tablet.pl | grep -v grep`

if [ "$prs" != "" ] ; then
read -r -a Words <<< $prs
kill -9 ${Words[1]}
fi

#Rotate the screen and fire off the appropriate Perl touchscreen script
case $angle in
normal | 0 )
xrandr -o normal
perl /drivers/tablet.pl &
;;
right | 90 )
xrandr -o right
perl /drivers/tablet.pl 90 &
;;
inverted | 180 )
xrandr -o inverted
perl /drivers/tablet.pl 180 &
;;
left | 270 )
xrandr -o left
perl /drivers/tablet.pl 270 &
;;
esac


You can replace '/drivers/tablet.pl' with the location of your touchscreen script.

Now take 'tablet5.en.pl' from here and modify is as follows:

Add the following code after the line 'use constant DIGITIZER=>(30.78,30.06);'

my $angle = '0';

if (not undef($ARGV)) {
$angle = $ARGV[0];
}


Replace the body of 'sub movemouse(@)' with:

    (my $x,my $y)=@_;
if (($x ne $prevx)||($y ne $prevy)) {

if ($angle eq '90' || $angle eq 'right') {
my $tmpx = $x;
$x = $y;
$y = (SCREEN)[0] - $tmpx;
}
elsif ($angle eq '180' || $angle eq 'inverted') {
$x = (SCREEN)[0] - $x;
$y = (SCREEN)[1] - $y;
}
elsif ($angle eq '270' || $angle eq 'left') {
my $tmpx = $x;
$x = (SCREEN)[1] - $y;
$y = $tmpx;
}

$prevx=$x;
$prevy=$y;
MoveMouseAbs($x,$y);
}


And you're done!

Now to get the touchscreen working at startup you need to add the line 'perl <path to touchscreen script> &' at the end of the file '/etc/gdm/Init/Default' (but before the exit command).

I would recommend putting links to the different screen rotations on your taskbar (there are some nice icons here) and also one to 'onboard', the very good onscreen keyboard that comes with Ubuntu.

Thursday 12 June 2008

Rails: Refreshing Multiple Partials in a Page

So you have a page with a load of partials in it, all of which have forms that update their own partials by AJAX calls (form_for_remote, submit_to_remote etc.) - no worries so far. But then what if you update a field in one partial that has a knock on effect to other partials e.g. a price in an order booking partial could change the total price in an order summary partial. It seems like a fairly normal thing to want to do but ActionView's support for this sort of stuff is rubbish!

An ActionView controller will automatically render a partial with the same name as itself unless it is given an alternative render command. The problem is that ActionView will not allow more than one render per method.

Using JSP's 'page.replace_html' call it is possible to do multiple 'innerHTML' type javascript replacements. However you first must render the partials to strings using the 'render_to_string' method as shown:


charges_partial = render_to_string(:partial => 'transactions/charges_and_payments', :object => @transaction)
order_partial = render_to_string(:partial => 'transactions/order_totals', :object => transaction)

render :update do |page|
page.replace_html 'charges_and_payments', charges_partial
page.replace_html 'order_totals', order_partial
end


You cannot call the render_to_string method from within the render block as they lose their scope and you get 'method not found' errors.

Remember though that including this method will mean that your default partial (the one with the same name as the controller method) will not render, you will have to add it manually to the call above.

But what if you have a set of partials that will need to be updated by several different calls - you don't want to repeat the same code in every method but you also can't do a redirect to a master function (as they aren't allowed after a render) and you can't include separate renderer method as ActionView counts that as being multiple renderers in the one call which it doesn't allow. Bugger.

Here's the way that I got round it.

First I made a master 'refresh partials' method:


class TransactionsController < transaction =" Transaction.find_by_id(params[:id])" pending_partial =" render_to_string(:partial"> 'transactions/pending_order', :object => @transaction)
order_partial = render_to_string(:partial => 'transactions/order_totals', :object => @transaction)
charges_partial = render_to_string(:partial => 'transactions/charges_and_payments', :object => @transaction)
comments_partial = render_to_string(:partial => 'transactions/comments', :object => @transaction)

render :update do |page|
page.replace_html 'pending_order', pending_partial
page.replace_html 'order_view_totals', order_partial
page.replace_html 'charges_and_payments', charges_partial
page.replace_html 'transaction_comments', comments_partial
end
end

end


Then (remember that I want these partials to be refreshed when a form in another is submitted) I added the following code to the partials that were being directly updated by their own forms. This will now call the refresh partials function whenever the parent partial is refreshed and the 'do_not_refresh_totals' variable is not set.


<% if not defined?(do_not_refresh_totals) or do_not_refresh_totals = true %>
<script type="text/javascript">
<%= remote_function(:url => { :controller => 'transactions', :action => 'refresh_partials', :id => transaction.id }) %>
</script>
<% end %>


The point of the 'do_not_refresh_totals' variable is that when the page is loaded initially I don't want a load of refresh calls to be immediately fired off - there would be no point as no data is being submitted so no changes are being made. So in the parent view where the partials are being initially rendered from you simply need something like the following:


<%= render :partial => "flight_booking", :locals => { :flight_booking => flight_booking, :do_not_refresh_totals => true } %>


Enjoy!

Note: Actually I've found a more intuitive way of doing this using separate RJS files. For any method in any controller, if you put an RJS file with the name of that method in that controller's view folder then its code will be executed immediately after the controller method has run and the code will have access to any instance variables you created in the that method. Put your 'replace_html' syntax in your RJS file and you're good to go. Easy!
This however does totally spam your view folders with RJS files...

Monday 9 June 2008

Cloning an EEEPC Drive

There are various tutorials on cloning disks, configuring grub, recompiling disk images etc but I've yet to see one that goes through step by step how to clone an EEEPC disk.
A recent project I was given for Radio Lollipop required me to configure an EEPC and then clone the disk to an SD card and make it bootable. This allowed us to be able to insert the card into any EEEPC and have it boot with our software and configuration.

Now the EEEPC is shipped with two partitions
sda1 is the root partition which is only ever mounted as ro and contains the base install
sda2 is the user partition and contains all the user files, additional apps and modifications.
When the machine is turned on unionfs is used to combine these partitions into one bootable drive
When you do a factory reset on an EEEPC it basically just blanks the user partition returning you to a base install

So I had two options available to me:

Clone only the user partition and make the EEEPC use the memory card as the user partition whist booting from the existing root partition
Advantages:
Can be done on a smaller memory card
Drawbacks:
Would have to create a new bootloader.
Susceptible to incompatibility with newer releases of the EEEPC.


Clone everything from both partitions and combine them correctly onto one memory card.
Advantages:
Grub and the boot image would already exist and would just need to be configured.
Copying all the data means that it would be immune to incompatibility issues on newer releases of the EEEPC.
Drawbacks:
Would have to merge the partitions somehow
Requires a much bigger memory card


I'll begin with my progress on cloning only the user partition.

First I downloaded a copy of DamnSmallLinux and burnt it to a CD. Then by booting from the CD I could access both drive partitions quite happily. DamnSmallLinux views the root partitions as 'hdc1' and the user partition as 'hdc2' (you can check this by simply running the command 'mount').

I ran this command to copy the user partition to my SD card (I got a 2Gb SD card which did the job fine - a 1GB may even do)

dd if=/dev/hdc2 of=/dev/sdb2


This took a while but once it was done I had my clone.
Then I copied the boot folder from the root partition (hdc1) to the SD card aswell.

I then setup the Grub bootloader on the SD card:

sudo grub
> find /boot/grub/stage1
##Pick the last hd it finds e.g. (hd3,0)
> root (hd3,0)
> setup (hd3)
> quit


Now toggle the SD card's 'bootable' flag using the following commands:

sudo fdisk /dev/sdb1
option 2
option a
partition 1


And finally edit your drive's /boot/grub/device.map to add
(hd1)   /dev/sdb
(hd2) /dev/sdc


Now you have yourself a bootable SD card

I then followed what is said here about setting up menu.1st and the initramfs image (go from the heading 'Changing the SD card so that the EeePC boots and uses the SD card for it's disk'. However I had to make a couple of changes to the instructions:

Where it says to copy the device files for sda1 and sda2 into your initramfs dev folder instead run these commands.

sudo mknod sdb1 b 8 17
sudo mknod sdb2 b 8 18
sudo mknod sdc1 b 8 33
sudo mknod sdc2 b 8 34


As firstly the copy command just plain doesn't work and secondly this will also give you access to sdc which is the USB

Also where it tells you to enter the sleep command into the init file you need to enter 25 or 30 in order to give it time to load the device drivers, otherwise when you boot it will be unable to mount the SD card or USB.

After you've zipped your initramfs image back up and copied it to the SD card you should be good to go. All you then need to do is press F2 at boot and change your boot order to default to your SD card and away you go!