August 8, 2010

N900 desktop widget (lucid dreaming reality check)

I Am sure many people have now seen inception and are at least aware of lucid dreaming, all though it did not go into the concept of reality checking the basic premises is simple to make you ask the question are you dreaming and to actually become aware that your dreaming.

Reality checking basically works by using some limitations of your mind while dreaming, while you are asleep dreaming your mind is creating an entire world for you to exist in, however only one hemisphere of your brain is engaged in this activity and it has limitations, things like reading are next to impossible because they use the other hemisphere of your brain and looking at something like your hand then away and looking at your hand again will make it look deformed because your mind lags and can not reproduce the scene fast enough, think of it as rendering lag on a computer.

This app was written to remind you todo a reality check by looking at your hand in the day looking away and then back again, by doing this repeatedly it should happen in a dream and because your asking the question are you dreaming and seeing your hand deform you will realise your dreaming, and either wake up in panic or take control of your dream.

The app is written in gtk for the maemo hildon desktop for the n900, it simply puts a widget on your desktop which asks are you dreaming and vibrates the phone at a set interval so you remember to do a reality check, it renders the text using cairo and uses gtk for the applications settings, and about dialog boxes.

First we need a few dependencies from the command line on your phone install pythonhildon-desktop and hildon-desktop-python-loader using the commands below.

apt-get install hildon-desktop-python-loader
apt-get install pythonhildon-desktop

After this we can create a .desktop file which will allow the application to be added from the phones widget menu save the code below in a file in called /usr/share/applications/hildon-home/realitycheck.desktop you can view other files in this folder to see what they contain.

[Desktop Entry]
Name=Reality Check
Comment=Reality Check, uced to help induce a lucid dreaming and simply vibrates to remind you to do a reality check
Type=python
X-Path=realitycheck.py
X-Multiple=true

Now for the main program, all the code is included in one file which should be placed in /usr/lib/hildon-desktop/realitycheck.py the code is below copy and paste it into the file you just created it can also be run manually by running python realitycheck.py then check you desktop for the application.

import os
import gtk
import glib
import osso #high end interface to dbus
import cairo#vector graphic drawing library
import hildon
import hildondesktop
from gtk import Window, Button, Widget, Image

#apt-get install hildon-desktop-python-loader
#apt-get install pythonhildon-desktop
#apt-get install hildon-desktop-python-loader
class realityCheckWidget(hildondesktop.HomePluginItem):
#applications settings
config_path='/home/user/.realitycheck'
vibrate=True
vibrateRepeat=15

def __init__(self):
hildondesktop.HomePluginItem.__init__(self)
self.load()

#enable the settings button, and function to call on click
self.set_settings(True)
self.connect("show-settings", self.show_options)

#the vibrate timeout callback
if self.vibrate==True:
self.timeout_handler = glib.timeout_add_seconds(60*self.vibrateRepeat, self.vibratePhone)

#request size of area to render text into.
self.set_size_request(660,200)

#draw scene def draw_cairo(self, cr): # This currently doesn't work
#set cairo surface for drawing we require an alpha layer
cr.set_source_rgba(1.0, 1.0, 1.0, 0.0)
cr.set_operator(cairo.OPERATOR_SOURCE)
cr.paint()

#choose font and style ie bold italic
cr.select_font_face("tahoma", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD);
#set the font size
cr.set_font_size(60);

#font colour, position and text
cr.set_source_rgb(65535,65535,65535)
cr.move_to(20, 50);
cr.show_text("Are you dreaming ?");

#second line of text use text path so we can have a fill and stroke
cr.set_source_rgb(0,0,0)
cr.move_to(20, 120);
cr.text_path("Reality Check")
cr.fill_preserve()
cr.set_source_rgb(65535,65535,65535)
cr.stroke()

#default option selection
def show_options(self, widget):
APP_TITLE="Reality Check"
dialog = gtk.Dialog("Reality Check Options", None, gtk.DIALOG_DESTROY_WITH_PARENT)

#add settings button to dialog
settings_button = hildon.Button(gtk.HILDON_SIZE_HALFSCREEN_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
settings_button.set_text("Settings", "Change the frequency")
settings_button.set_alignment(0,0,0,0)
settings_button.connect('clicked', self.show_settings)
#add license button to dialog
about_button = hildon.Button(gtk.HILDON_SIZE_HALFSCREEN_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
about_button.set_text("About", "More about Author, Copyright and License")
about_button.set_alignment(0,0,0,0)
about_button.connect("clicked", self.show_about)

#pad the buttons into a box for nice alignment
hboxRow = gtk.HBox()
hboxRow.pack_start(settings_button, True, True, 0)
hboxRow.pack_start(about_button, True, True, 0)

#add button row to the dialog and display it
dialog.vbox.pack_start(hboxRow, True, True, 0) dialog.show_all()
dialog.run()
dialog.destroy()

#Setup the settings UI , store changed parameters def show_settings(self, widget):
#create a dialog and attach our buttons to the dialog
dialog = gtk.Dialog("Settings", None, gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR)
btnSave = dialog.add_button(gtk.STOCK_SAVE, gtk.RESPONSE_OK)

main_vbox = gtk.VBox()
ts = hildon.TouchSelector()
ts.add(main_vbox)

#create the options and labels btn1mins = hildon.GtkRadioButton(gtk.HILDON_SIZE_HALFSCREEN_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, None)
btn1mins.set_label("15 Mins")
btn1mins.set_mode(False)

btn2mins = hildon.GtkRadioButton(gtk.HILDON_SIZE_HALFSCREEN_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, btn1mins)
btn2mins.set_label("30 Mins")
btn2mins.set_mode(False)

btn3mins = hildon.GtkRadioButton(gtk.HILDON_SIZE_HALFSCREEN_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, btn1mins)
btn3mins.set_label("1 Hours")
btn3mins.set_mode(False)

btn4mins = hildon.GtkRadioButton(gtk.HILDON_SIZE_HALFSCREEN_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, btn1mins)
btn4mins.set_label("2 Hours")
btn4mins.set_mode(False)

#toggle vibrate on and off
btnVibrate = hildon.GtkToggleButton(gtk.HILDON_SIZE_HALFSCREEN_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
btnVibrate.set_label("Vibrate")
btnVibrate.set_mode(False)

#what options are currently set, highlight the currently active options
if self.vibrateRepeat == 15:
btn1mins.set_active(True)
elif self.vibrateRepeat == 30:
btn2mins.set_active(True)
elif self.vibrateRepeat == 60:
btn3mins.set_active(True)
elif self.vibrateRepeat == 120:
btn4mins.set_active(True)
else:
btn1mins.set_active(True) #Fallback

if self.vibrate==True:
btnVibrate.set_active(True)

#pack the buttons into a horizontal box
hBoxLayout = gtk.HBox()
hBoxLayout.pack_start(btn1mins, True, True, 0)
hBoxLayout.pack_start(btn2mins, True, True, 0)
hBoxLayout.pack_start(btn3mins, True, True, 0)
hBoxLayout.pack_start(btn4mins, True, True, 0)
hBoxLayout.pack_end(btnVibrate, True, True, 0)
main_vbox.pack_start(hBoxLayout, True, True, 10)

dialog.vbox.add(ts)
dialog.show_all()
response = dialog.run()
#Parse the updated settings
if response == gtk.RESPONSE_OK:

camera_buttons = btn1mins.get_group()
self.vibrate=btnVibrate.get_active()
for button in camera_buttons:

selected = button.get_active()
if selected == True:
label=button.get_label()
if label=='15 Mins':
self.vibrateRepeat=15
if label=='30 Mins':
self.vibrateRepeat=30
if label=='1 Hours':
self.vibrateRepeat=60
if label=='2 Hours':
self.vibrateRepeat=120

#clear vibrate event and rest if we need to
glib.source_remove(self.timeout_handler)
if self.vibrate==True:
self.timeout_handler = glib.timeout_add_seconds(60*self.vibrateRepeat, self.vibratePhone)

dialog.destroy()
self.save()
#Show the about dialog
def show_about(self, widget):
dialog = gtk.AboutDialog()
dialog.set_title("About")
dialog.set_name('Reality Check')
dialog.set_version('1.0')
dialog.set_copyright("Copyright 2010 Oliver Marks")
dialog.set_authors(["Oliver Marks ",""])
dialog.set_comments("All logos and trademarks are property of their respective owners and are used for informational purposes only.")
dialog.set_license("""This program is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see
.""")
dialog.set_wrap_license(True)
#display the license information dialog dialog.show_all()
dialog.run()
dialog.destroy()
#save settings file
def save(self):
fp = open(self.config_path,'w')
if self.vibrate==True:
fp.write('vibrate:on ')
else:
fp.write('vibrate:off ')
fp.write('mins:'+str(self.vibrateRepeat)+" ")
fp.close()

#load settings file
def load(self):
if not os.path.exists(self.config_path):
fp=open(self.config_path,'w')
fp.write('')
fp.close()

fp = open(self.config_path)
for line in fp.readlines():
sp=line.strip(' ').split(':')

if sp[0]=='vibrate':
if sp[1]=='on':
self.vibrate=True
else:
self.vibrate=False

if sp[0]=='mins':
self.vibrateRepeat=int(sp[1])
fp.close()

#draw scene when re recieve an expose event ie the scene needs redrawing
def do_expose_event(self, event):
cr = self.window.cairo_create()
cr.rectangle(event.area.x, event.area.y,event.area.width, event.area.height)
cr.clip()
self.draw_cairo(cr)

#setup screen so we can draw on it with cairo
def do_realize(self):
screen = self.get_screen()
self.set_colormap(screen.get_rgba_colormap())
self.set_app_paintable(True)
hildondesktop.HomePluginItem.do_realize(self)

#this code will cause the led on the phone to flash
def led_flash(self):
_ENABLE_LED = 'req_led_pattern_activate'
_DISABLE_LED = 'req_led_pattern_deactivate'
_LED_PATTERN = 'PatternCommunicationIM'
rpc.rpc_run(_MCE_SERVICE, _MCE_REQUEST_PATH,_MCE_REQUEST_IF,_ENABLE_LED,rpc_args=(_LED_PATTERN,"",""),use_system_bus=True)
return True

#cause the phone to vibrate
def vibratePhone(self):
osso_c = osso.Context("osso_test_app", "0.0.1", False)

_MCE_SERVICE = 'com.nokia.mce'
_MCE_REQUEST_PATH = '/com/nokia/mce/request'
_MCE_REQUEST_IF = 'com.nokia.mce.request'

_VIBRATE = 'req_vibrator_pattern_activate'
_VIBRATE_PATTERN= 'PatternChatAndEmail'


rpc = osso.Rpc(osso_c)

rpc.rpc_run(_MCE_SERVICE, _MCE_REQUEST_PATH,_MCE_REQUEST_IF,_VIBRATE,rpc_args=(_VIBRATE_PATTERN,"",""),use_system_bus=True)
return True

hd_plugin_type = realityCheckWidget


# Allow the widget to run on its own, without the hildon desktop loader
if __name__ == "__main__":
import gobject
gobject.type_register(realityCheckWidget)
obj = gobject.new(realityCheckWidget, plugin_id="plugin_id")
obj.show_all()
gtk.main()

Below is a screen shot of the final app running on my n900 phone, hopefully a nice little example of writing an app for the phone and shows how similar the apps are to writing standard gnome applications.

Click Here!