Commits

gamedude  committed 6320240

- The launcher application for starting pygame apps
-- TODO: Symbian does not let one share the open (font) files thus there can be only 1 active font at a time. This means that fonts cannot be cached. The images must be cached instead. Menu update is currently noticeably slow because each time a font is used, it is loaded from file.

- liquid_s60.py, the liguid effect

  • Participants
  • Parent commits 204716a
  • Branches symbian_s60

Comments (0)

Files changed (12)

File symbian/BuildPygameRelease.py

     args = { "applications" : "",
              "capabilities" : CAPABILITIES,
              "builtin"      : "sysinfo,socket",
+             #"pyds"         : "gles",
              "basename"     : "pygame_python",            
              "uidbase"      : hex(UID_BASE).replace("L",""),             
              "sisappname"   : '"Python for Pygame"',
              # Convert to int or may be converted to octal due to zero at beginning
              'sisversion'   : '"(1,%d,%d%s)"' % ( int(version[2:4]), int( version[4:6]),version[6:]),
              'pythonsis'    : sisname,
-             'libpath'      : "data/pygame/libs",
+             'libpath'      : "data/pygame/libs",             
              }
     
     # Add certificate stuff
     sisname   = ""  
     if config.build_python:
         curdir = os.getcwd()
-        os.chdir(config.pys60_ce_src)                
-        dobuild(args)    
+        os.chdir(config.pys60_ce_src)   
+        a = args.copy()
+        a["gcce_options"] = '"-O2 -fno-unit-at-a-time"'             
+        dobuild(a)    
         os.chdir(curdir)
     
         sisname = "python_for_pygame_%s_signed.sisx" % version
         pys60_sis = os.path.join( config.pys60_ce_src, sisname )
                 
         # Copy the sis to current directory
-        import shutil
-        shutil.copyfile(pys60_sis, sisname)
+        if os.path.exists(pys60_sis):
+            import shutil
+            shutil.copyfile(pys60_sis, sisname)
         
         args['pythondll'] =  args['basename']
          

File symbian/SConscript.app.py

 #    to_package( source = x, target = "data/pygame")
 
 to_package( source = "app/pygame_main.py", target = "data/pygame" )
-#to_package( source = "app/ambient.ogg", target = "data/pygame" )
+to_package( source = "app/launcher/logo.jpg", target = "data/pygame/launcher" )
+to_package( source = "app/launcher/pygame_launcher.py", target = "data/pygame/launcher" )
+to_package( source = "app/apps/liquid_s60.py", target = "data/pygame/apps" )
 
 

File symbian/SConscript.jpeg.py

 # This file is generated with mmp2sconscript
 from scons_symbian import *
 
-Import("TARGET_NAME UID3 PACKAGE_NAME")
+Import("TARGET_NAME UID3 PACKAGE_NAME CAPABILITIES")
 
 # Built as dll because needed by SDL and the application
 target     = TARGET_NAME
     sysincludes = sysincludes,
     libraries   = libraries,
     defines     = defines,
-    uid3 = uid3, 
+    capabilities = CAPABILITIES,
+    uid3 = uid3,
     package = PACKAGE_NAME,
 )
 

File symbian/SConstruct

 
 
 EMBEDDED_PYTHON_SIS = ARGUMENTS.get( "pythonsis", "none" )
-if EMBEDDED_PYTHON_SIS.lower() != "none":
+if EMBEDDED_PYTHON_SIS.lower() != "none" and COMPILER != COMPILER_WINSCW:
     # Read the UID
     f=open( EMBEDDED_PYTHON_SIS,'rb')
     # First 16 bytes are for UIDs
     uids = struct.unpack("IIII", data )
     EMBEDDED_PYTHON_SIS_UID = hex(uids[2]).replace("L","")
     print "Read UID %s from '%s'" % ( EMBEDDED_PYTHON_SIS_UID, EMBEDDED_PYTHON_SIS  )
-
+else:
+    EMBEDDED_PYTHON_SIS = ""
+    EMBEDDED_PYTHON_SIS_UID = 0x0
+      
 Export("PACKAGE_NAME PYTHON_LIB_NAME PATH_PY_LIBS SDL_DLL_NAME CAPABILITIES PYTHON_INCLUDE")
 
 # Build external libraries. Conditional because needed only once and takes extra time after that.
     "appname" : PACKAGE_TITLE,
     'cert':      CERT,
     'key':       KEY,
-    'passwd':    PASSPHRASE
-}
-
-preppydata = {
-      "EMBEDDED_PYTHON_SIS"     : EMBEDDED_PYTHON_SIS, 
-      "EMBEDDED_PYTHON_SIS_UID" : EMBEDDED_PYTHON_SIS_UID,
+    'passwd':    PASSPHRASE,
+    "EMBEDDED_PYTHON_SIS"     : EMBEDDED_PYTHON_SIS, 
+    "EMBEDDED_PYTHON_SIS_UID" : EMBEDDED_PYTHON_SIS_UID,
 }
 
 Depends( PACKAGE_NAME, EMBEDDED_PYTHON_SIS )
-SymbianPackage(PACKAGE_NAME, pkgargs=pkgargs, pkgtemplate = pkgtemplate, preppydata = preppydata )
+SymbianPackage(PACKAGE_NAME, pkgargs=pkgargs, pkgtemplate = pkgtemplate )
 
 

File symbian/app/apps/liquid_s60.py

+#!/usr/bin/env python
+
+"""This examples demonstrates a simplish water effect of an
+image. It attempts to create a hardware display surface that
+can use pageflipping for faster updates. Note that the colormap
+from the loaded GIF image is copied to the colormap for the
+display surface.
+
+This is based on the demo named F2KWarp by Brad Graham of Freedom2000
+done in BlitzBasic. I was just translating the BlitzBasic code to
+pygame to compare the results. I didn't bother porting the text and
+sound stuff, that's an easy enough challenge for the reader :]"""
+
+import pygame, os
+from pygame.locals import *
+from math import sin
+
+THISDIR = os.path.dirname( __file__ )
+DISPLAY_SIZE = 240, 320
+def main():
+    #initialize and setup screen
+    pygame.init()
+    if os.name == "e32":
+        size = pygame.display.list_modes()[0]
+    else:
+        size =  DISPLAY_SIZE
+        
+    screen = pygame.display.set_mode(size)#, DOUBLEBUF)#, HWSURFACE)
+
+    #load image and quadruple
+    imagename = os.path.join( THISDIR, '..', 'launcher', 'logo.jpg')
+    bitmap = pygame.image.load(imagename)
+    bitmap = pygame.transform.scale(bitmap, size )
+    #bitmap = pygame.transform.scale2x(bitmap)
+    #bitmap = pygame.transform.scale2x(bitmap)
+    s = bitmap.get_size()
+    
+    #get the image and screen in the same format
+    if screen.get_bitsize() == 8:
+        screen.set_palette(bitmap.get_palette())
+    else:
+        bitmap = bitmap.convert()
+
+    #prep some variables
+    anim = 0.0
+
+    #mainloop
+    xblocks = range(0, s[0], 20)
+    yblocks = range(0, s[1], 20)
+    stopevents = QUIT, KEYDOWN, MOUSEBUTTONDOWN
+    clock = pygame.time.Clock()
+    while 1:
+        for e in pygame.event.get():
+            if e.type in stopevents:
+                return
+
+        anim = anim + 0.2
+        for x in xblocks:
+            xpos = (x + (sin(anim + x * .01) * 15)) + 20
+            for y in yblocks:
+                ypos = (y + (sin(anim + y * .01) * 15)) + 20
+                screen.blit(bitmap, (x, y), (xpos, ypos, 20, 20))
+
+        pygame.display.flip()
+        clock.tick(30)
+
+if __name__ == '__main__': main()
+
+
+
+"""BTW, here is the code from the BlitzBasic example this was derived
+from. i've snipped the sound and text stuff out.
+-----------------------------------------------------------------
+; Brad@freedom2000.com
+
+; Load a bmp pic (800x600) and slice it into 1600 squares
+Graphics 640,480
+SetBuffer BackBuffer()
+bitmap$="f2kwarp.bmp"
+pic=LoadAnimImage(bitmap$,20,15,0,1600)
+
+; use SIN to move all 1600 squares around to give liquid effect
+Repeat
+f=0:w=w+10:If w=360 Then w=0
+For y=0 To 599 Step 15
+For x = 0 To 799 Step 20
+f=f+1:If f=1600 Then f=0
+DrawBlock pic,(x+(Sin(w+x)*40))/1.7+80,(y+(Sin(w+y)*40))/1.7+60,f
+Next:Next:Flip:Cls
+Until KeyDown(1)
+"""

File symbian/app/launcher/logo.jpg

Added
New image

File symbian/app/launcher/pygame_launcher.py

+"""
+pygame launcher for S60 - Application for launching others
+"""
+
+from glob import glob
+
+import math
+
+import sys
+import os
+
+import pygame
+from pygame.locals import *
+from pygame import constants
+
+BLACK = 0, 0, 0, 255
+WHITE = 255,255,255,255
+TITLE_BG     = 0, 255, 0, 75
+TITLE_STROKE = 0, 255, 0, 200
+DISPLAY_SIZE = 240, 320
+MENU_BG = 0, 255, 0, 75
+ITEM_UNSELECTED_TEXT = 0, 128, 0, 128
+ITEM_SELECTED_TEXT = 0, 255, 0, 128
+ITEM_UNSELECTED = 0, 128, 0, 128
+ITEM_SELECTED   = TITLE_BG
+
+THISDIR = os.path.dirname( __file__ )
+
+def load_image(name, colorkey=None):
+    """ Image loading utility from chimp.py """
+    fullname = os.path.join(THISDIR, name)
+    try:
+        image = pygame.image.load(fullname)
+    except pygame.error, message:
+        print 'Cannot load image:', name
+        raise SystemExit, message
+    image = image.convert()
+    if colorkey is not None:
+        if colorkey is -1:
+            colorkey = image.get_at((0,0))
+        image.set_colorkey(colorkey, RLEACCEL)
+    return image, image.get_rect()
+
+class SystemData:
+    """ Common resources """
+    def __init__(self):
+        self.screen   = None
+        self.ticdiff  = 0
+        self.tics     = 0
+        
+    def get_font_title(self):
+        return pygame.font.Font(None, 36)
+    def get_font_normal(self):
+        return pygame.font.Font(None, 25)
+        #self.font_normal   = self.font_title#
+    def get_font_normal_b(self):
+        f = self.get_font_normal()
+        f.set_bold(True)
+        return f
+    def get_font_small(self):
+        return pygame.font.Font(None, 18)
+        
+class Background(pygame.sprite.Sprite):
+    """Main background"""
+    def __init__(self, sysdata):
+        pygame.sprite.Sprite.__init__(self) 
+        
+        self.sysdata = sysdata
+        
+        self.screen = sysdata.screen
+        screen_size = self.screen.get_size()
+        #middle = #screen_size[0] / 2., screen_size[1] / 2.
+        
+        self.background = pygame.Surface(screen_size, SRCALPHA)
+        self.background.fill(BLACK)
+        
+        self.rect       = self.background.get_rect()
+        middle = self.rect.center
+        
+        self.img_logo, r = load_image( "logo.jpg")
+        self.img_pos = [ middle[0] - r.w / 2, middle[1] - r.h / 2 ]
+        
+        self.alpha = pygame.Surface(r.size, SRCALPHA)
+        
+        self.alphaval = 200.
+        self.alphadir = -20. # per second
+      
+    def update_alphaval(self):
+        """ Update the visibility of the logo """
+        min = 200.
+        max = 245.
+        
+        s = self.sysdata.ticdiff / 1000.
+        
+        self.alphaval += s * self.alphadir
+        # + (abs(self.alphadir) / self.alphadir) * ( (max - self.alphaval) / ( max -min )) 
+        
+        if self.alphaval > max:
+            self.alphaval = max
+            self.alphadir = -self.alphadir
+            
+        if self.alphaval < min:
+            self.alphaval = min
+            self.alphadir = -self.alphadir
+        
+    def update(self):
+        
+        self.update_alphaval()
+        
+        self.background.fill(BLACK)
+        
+        self.alpha.fill( (0,0,0,self.alphaval) )
+        
+        self.background.blit( self.img_logo, self.img_pos )
+        self.background.blit(self.alpha, self.img_pos )
+        
+
+class TextField(pygame.sprite.Sprite):
+    """ Shows text """
+    MODE_NONE   = 0
+    MODE_CENTER = 1
+    
+    def __init__(self, parent, sysdata, exit_callback, title = "", text = "", mode = MODE_NONE ):
+        pygame.sprite.Sprite.__init__(self)
+        
+        self.sysdata = sysdata
+        self.parent = parent
+        self._title = title
+        self.title_changed = True
+        
+        self._text = text
+        self.text_changed = True
+        self._selected_index = 0
+        
+        self.mode = mode
+        
+        self.exit_callback = exit_callback
+        
+    def update_text(self):
+        """ Redraw text contents """
+        if not self.text_changed: return
+        
+        text = self._text
+        
+        # Below title
+        startposy = self.titlesurface.get_size()[1] + 20
+        startposx = 10
+        
+        # Position on parent
+        self.itemspos = ( 0, startposy )
+        
+        psize = self.parent.get_size()
+        size = ( psize[0], psize[1] - startposy )
+        surf = pygame.Surface(size, SRCALPHA)
+        
+        # Create text contents
+        rect = pygame.Rect( 5, 0, size[0]-10, size[1]-5)
+        pygame.draw.rect(surf, MENU_BG, rect)
+        pygame.draw.rect(surf, TITLE_STROKE, rect, 2)
+        
+        #text = textwrap.dedent(text)
+        font = self.sysdata.get_font_small()
+        
+        lines = text.split("\n")
+        height = font.get_height()
+        posy  = height
+        for line in lines:
+            line = line.strip()
+            text = font.render(line, 1, (0,255,0) )
+            if self.mode == TextField.MODE_CENTER:
+                x,y = text.get_size()
+                x = size[0] - x
+                x /= 2
+            
+            surf.blit( text, (x,posy) )
+            posy += height
+        
+        self.textsurface = surf
+        self.text_changed = False
+        
+    def update_title(self):
+        """ Redraw title text """
+        if not self.title_changed: return
+            
+        text = self.sysdata.get_font_title().render(self._title, 1, (0, 255, 0))
+        textpos = text.get_rect()
+        textpos.centerx = self.parent.get_rect().centerx
+        textpos.centery = textpos.size[1] / 2 + 7
+        
+        self.size = size = self.parent.get_size()
+        size = ( size[0], 40 )
+
+        surf = titlebg = pygame.Surface(size, SRCALPHA)
+        self.titlebgrect = titlebgrect = pygame.Rect( 5, 5, size[0]-10, size[1]-10)
+        pygame.draw.rect(surf, TITLE_BG, titlebgrect)
+        pygame.draw.rect(surf, TITLE_STROKE, titlebgrect, 2)
+        surf.blit(text, textpos )
+        
+        self.title_changed = False
+        self.titlesurface = surf
+        # Position on parent
+        self.titlepos = (0,0)
+        
+    def update(self):
+        self.update_title()
+        self.update_text()
+        
+        self.parent.blit(self.titlesurface, self.titlepos )
+        self.parent.blit(self.textsurface, self.itemspos )
+    
+    def exit(self):
+        self.exit_callback()
+        
+    def handleEvent(self, event ):
+        """ Exit on any key """
+        if event.type == pygame.KEYDOWN:
+            self.exit()
+            return True
+        
+        return False
+    
+class Menu(pygame.sprite.Sprite):
+    
+    def __init__(self, parent, sysdata, title, items, cancel_callback):
+        pygame.sprite.Sprite.__init__(self)
+        
+        self.sysdata = sysdata
+        self.parent = parent
+        self._title = title
+        self.title_changed = True
+        
+        self._items = items
+        self.items_changed = True
+        self._selected_index = 0
+        
+        #: Index of the topmost visible item
+        self.visibletop = 0
+        #: How many items the list can display
+        self.shownitems = 0
+        
+        #: Callback called at exit
+        self.cancel_callback = cancel_callback
+        print cancel_callback
+        
+    def _set_selection(self,index):
+        self._selected_index = index
+        self.items_changed = True
+    
+    def _get_selection(self): return self._selected_index
+    selection = property(fget=_get_selection, fset=_set_selection )
+    
+    def select_next_item(self):
+        """ Select next item from list. Loops the items """
+        last = len(self._items) - 1
+        if last < 1: return
+        
+        next = self._selected_index + 1
+        if next > last:
+            next = 0
+        self.selection = next
+        
+        self.update_visible()
+        
+    def select_prev_item(self):
+        """ Select previous item from list. Loops the items """
+        last = len(self._items) - 1
+        if last < 1: return
+        
+        first = 0
+        next = self._selected_index - 1
+        if next < first:
+            next = last
+        self.selection = next
+        
+        self.update_visible()
+    
+    def update_visible(self):
+        """ Updates position of the topmost visible item in the list """
+        diff = abs(self.visibletop - self._selected_index)
+        if diff >= self.shownitems:
+            self.visibletop = max(0,min( self._selected_index - self.shownitems+1, self._selected_index ))
+        
+    def select_item(self):
+        """ Handle item selection by invoking its callback function """
+        title, callback,args = self._items[self._selected_index]
+        callback(*args)
+    
+    def _set_title(self, title):
+        self._title = title
+        self.title_changed = True
+        
+    def _get_title(self): return self._title
+    title = property(fget=_get_title, fset=_set_title)
+    
+    def _set_items(self, title): 
+        self._items = items
+        self.items_changed = True
+        
+    def _get_items(self): return self._items
+    items = property(fget=_get_items, fset=_set_items)
+    
+    def cancel(self):
+        cb,args = self.cancel_callback
+        cb(*args)
+    
+    def handleEvent(self, event ):
+        if event.type == pygame.KEYDOWN:
+            if event.key == constants.K_DOWN:
+                self.select_next_item()
+                return True
+            
+            elif event.key == constants.K_UP:
+                self.select_prev_item()
+                return True
+            
+            if event.key == constants.K_RETURN:
+                self.select_item()
+                return True
+            
+            if event.key == constants.K_ESCAPE:
+                self.cancel()
+                return True
+            
+        return False
+    
+    def clear(self):
+        " Remove the used surfaces from memory "
+        self.itemsurface = None
+        self.titlesurface = None
+        self.items_changed = True
+        self.title_changed = True
+    
+    def max_items_shown(self):
+        """ Calculate the amount of items that fit inside the list """
+
+        height = self.sysdata.get_font_normal().get_height() * 1.5
+        h = self.itemsurface.get_height()
+        return int(round((h / height))) - 1
+     
+    def update_items(self):
+        """ Update list item surface """
+        if not self.items_changed: return
+        
+        items = self._items
+        
+        # Below title
+        startposy = self.titlesurface.get_size()[1] + 20
+        startposx = 10
+        self.itemspos = ( 0, startposy )
+        
+        psize = self.parent.get_size()
+        size = ( psize[0], psize[1] - startposy)
+        surf = pygame.Surface(size, SRCALPHA)
+        self.itemsurface = surf
+        self.shownitems  = self.max_items_shown()
+        
+        # Create and cache the list background
+        rect = pygame.Rect( 5, 0, size[0]-10, size[1]-5)
+        
+        pygame.draw.rect(surf, MENU_BG, rect)
+        pygame.draw.rect(surf, TITLE_STROKE, rect, 2)
+        
+        startposy = 0
+        
+        self.visibletop = min(self.visibletop, self._selected_index)
+        maximumpos = min(len(items), self.visibletop + self.shownitems )        
+        height = self.sysdata.get_font_normal().get_height()
+        spaceheight = height + 10
+        font = None
+        for x in xrange(self.visibletop,maximumpos):
+            i,cb,args = items[x]
+            #print i      
+            if x == self._selected_index:                 
+                del font # Close the font file
+                font = self.sysdata.get_font_normal_b()
+                color = ITEM_SELECTED_TEXT
+                bgcolor = ITEM_SELECTED
+            else:
+                del font # Close the font file
+                font = self.sysdata.get_font_normal()
+                color = ITEM_UNSELECTED_TEXT
+                bgcolor = ITEM_UNSELECTED
+            
+            s = ( size[0]-startposx*2- 15, spaceheight )
+            pos  = ( startposx, startposy )
+                        
+            # Draw text
+            text = font.render(i, 1, color)
+            textpos = text.get_rect()
+            textpos.centerx = self.parent.get_rect().centerx
+            textpos.centery = pos[1] + s[1] / 2
+            
+            # Add to list            
+            surf.blit( text, textpos )
+            startposy = startposy + height
+        
+        self.items_changed = False
+        
+    
+    def update_title(self):
+        """ Update title surface """
+        if not self.title_changed: return
+        
+        self.text = text = self.sysdata.get_font_title().render(self._title, 1, (0, 255, 0))
+        self.textpos = textpos = text.get_rect()
+        textpos.centerx = self.parent.get_rect().centerx
+        textpos.centery = textpos.size[1] / 2 + 7
+        
+        self.size = size = self.parent.get_size()
+        size = ( size[0], 40 )
+
+        surf = titlebg = pygame.Surface(size, SRCALPHA)
+        self.titlebgrect = titlebgrect = pygame.Rect( 5, 5, size[0]-10, size[1]-10)
+        pygame.draw.rect(surf, TITLE_BG, titlebgrect)
+        pygame.draw.rect(surf, TITLE_STROKE, titlebgrect, 2)
+        surf.blit(self.text, textpos )
+        
+        self.title_changed = False
+        
+        self.titlesurface = surf
+        # Position on parent
+        self.titlepos = (0,0)
+        
+    def update(self):                    
+        self.update_title()
+        self.update_items()
+        
+        self.parent.blit( self.titlesurface, self.titlepos )
+        self.parent.blit( self.itemsurface,  self.itemspos )
+        
+class Application(object):
+    def __init__(self):
+        if os.name == "e32":
+            size = pygame.display.list_modes()[0]
+            self.screen = pygame.display.set_mode(size, SRCALPHA)
+        else:
+            self.screen = pygame.display.set_mode( DISPLAY_SIZE, SRCALPHA ) 
+        
+        self.sysdata = SystemData()
+        self.sysdata.screen = self.screen
+        
+        self.background = Background(self.sysdata)
+        
+        items = [("Applications", self.mhApplications,()),
+                 ("Settings",self.mhSettings,()), 
+                 ("About",self.mhAbout,()),
+                 ("Exit",self.mhExit,()), ]
+        self._main_menu = Menu(self.background.background, self.sysdata, 
+                        title = "pygame launcher",
+                        items = items,
+                        cancel_callback = ( self.mhExit, () )
+                        )
+        self.focused = self._main_menu
+        self.sprites = pygame.sprite.OrderedUpdates()
+        self.sprites.add( self.background )
+        self.sprites.add( self.focused )
+        self.running = True
+        self.clock = pygame.time.Clock()
+        
+        self.app_to_run = None
+        
+        #: Updated by foreground event
+        self.isForeground = True
+        
+    def initialize(self):
+        pass
+    
+    def run(self):
+        """ Main application loop """
+        self.initialize()       
+        
+        self.sysdata.tics = pygame.time.get_ticks()
+        
+        eventhandler = self.handleEvent
+        while self.running:
+            
+            for event in pygame.event.get():
+                print event
+                eventhandler(event)
+            
+            if self.isForeground:
+                self.sprites.update()
+                
+                self.screen.blit(self.background.background, (0,0))
+                
+                pygame.display.flip()
+                
+                self.clock.tick(22)
+                
+            else:
+                # Longer delay when in backround
+                self.clock.tick(1)
+
+            tics = pygame.time.get_ticks()
+            self.sysdata.ticdiff = tics - self.sysdata.tics
+            self.sysdata.tics = tics
+            
+        return self.app_to_run
+        
+    def mhLaunchApplication(self, app_path):
+        """ Menu handler for application item """
+        
+        if app_path is None:
+            # Restore pygame launcher menu
+            self.focused.clear()
+            self.sprites.remove(self.focused)
+            self.sprites.add(self._main_menu)
+            self.focused = self._main_menu
+            return
+        
+        self.app_to_run = app_path
+        self.running = 0
+        
+    def mhApplications(self):
+        """ Menu handler for Applications item """
+        
+        join = os.path.join
+        appdir = join( THISDIR, "..", "apps" )
+        apps = glob( join( appdir, "*.py" ) )
+        apps += glob( join( appdir, "*.pyc" ) )
+        apps += glob( join( appdir, "*.pyo" ) )
+
+        items = []
+        for a in apps:
+            name = os.path.basename(a)
+            name = ".".join( name.split(".")[:-1])
+            if len(name) == 0: continue
+            
+            i = ( name, self.mhLaunchApplication, (a,) )
+            items.append(i)
+            
+        items.append( ("Back", self.mhLaunchApplication, (None,)) )
+        self.sprites.remove(self.focused)
+        self.focused = Menu(self.background.background, self.sysdata, 
+                        title = "Applications",
+                        items = items,
+                        cancel_callback = ( self.mhLaunchApplication, (None,) )
+                        )
+        self.sprites.add(self.focused)
+        
+    def mhExit(self):
+        """ Menu handler for exit item """
+        self.running = 0
+        
+    def mhSettings(self):
+        """ Menu handler for settings item """
+        self.sprites.remove(self.focused)
+        self.focused = TextField(self.background.background, self.sysdata,
+                        exit_callback = self._exitAbout,
+                        title = "Settings",
+                        text = "Begone! Nothing to see here!",
+                        mode = TextField.MODE_CENTER
+                        )
+        self.sprites.add(self.focused)
+    
+    def _exitAbout(self):
+        """ About view's exit handler"""
+        
+        self.sprites.remove(self.focused)
+        self.sprites.add(self._main_menu)
+        self.focused = self._main_menu
+        
+    def mhAbout(self):
+        """ Menu handler for about item """
+        self._main_menu = self.focused
+        text = """
+        -= pygame launcher =-
+        
+        
+        www.pygame.org
+        www.launchpad.net/pys60community
+        
+        Author: Jussi Toivola
+        
+        """
+        
+        self.sprites.remove(self.focused)
+        self.focused = TextField(self.background.background, self.sysdata,
+                        exit_callback = self._exitAbout,
+                        title = "About",
+                        text = text,
+                        mode = TextField.MODE_CENTER
+                        )
+        self.sprites.add(self.focused)
+        
+    def isExitEvent(self, event ):
+        """ @return True if event causes exit """
+        
+        #if event.type == pygame.KEYDOWN:     
+            # K_ESCAPE = Right softkey       
+            #if event.key == pygame.constants.K_ESCAPE:
+                #self.running = 0
+                #return True
+        if event.type == pygame.QUIT:
+            return True
+        
+        return False
+        
+    def handleEvent(self, event ):
+        if self.isExitEvent(event):
+            print "Exit event received!"
+            self.running = 0
+            return
+        
+        if self.isForeground:
+            if self.focused is not None:
+                handled = self.focused.handleEvent(event)
+                if not handled:
+                    # K_ESCAPE = Right softkey       
+                    if event.type == pygame.KEYDOWN \
+                    and event.key == constants.K_ESCAPE:
+                        self.running=0
+    
+def start():
+    """ Start pygame launcher """
+         
+    pygame.init() 
+    while True:
+                            
+        a = Application()
+        # The executable is received
+        path_to_app = a.run()
+                  
+        # Clear cyclic references and the launcher out of the way        
+        del a.background.sysdata
+        del a.background
+        del a._main_menu.sysdata
+        del a._main_menu.parent
+        del a._main_menu._items
+        del a._main_menu
+        del a.focused
+        del a.sysdata
+        a.sprites.empty()
+        del a
+        
+        if path_to_app:
+            
+            # Run the application and restart launcher after app is completed.
+            try:
+                os.chdir(os.path.dirname(path_to_app))
+                execfile(path_to_app, {'__builtins__': __builtins__,
+                                       '__name__': '__main__',
+                                       '__file__': path_to_app,
+                                       'pygame' : pygame }
+                )
+            except:
+                import traceback
+                traceback.print_exc()
+                sys.stdout.flush()
+                        
+            
+        else:
+            # Exit launcher
+            break
+        
+    pygame.quit()
+    
+if __name__ == "__main__":
+    start()

File symbian/app/pygame.rss

 RESOURCE EIK_APP_INFO
     {
     menubar=r_app_menubar;
-    cba=R_AVKON_SOFTKEYS_OPTIONS_EXIT;
+    cba=R_AVKON_SOFTKEYS_EMPTY;
     }
 
 RESOURCE MENU_BAR r_app_menubar

File symbian/app/pygame_app.cpp

 
 #include "pygame.hrh"
 
+#include "logmanutils.h"
+
 const TUid KUidPygameApp =
 { __UID3__ };
 
 	virtual void DoExit(TInt aErr) = 0;
 };
 
-class CExitWait : public CActive
+class CExitWait: public CActive
 {
 public:
 	CExitWait(MExitWait& aWait);
 	TRequestStatus* iStatusPtr;
 };
 
-class CSDLWin : public CCoeControl
+class CSDLWin: public CCoeControl
 {
 public:
 	void ConstructL(const TRect& aRect);
 	void Draw(const TRect& aRect) const;
 };
 
-class CSdlApplication : public CAknApplication
+class CSdlApplication: public CAknApplication
 {
 private:
 	// from CApaApplication
 	TUid AppDllUid() const;
 };
 
-class CSdlAppDocument : public CAknDocument
+class CSdlAppDocument: public CAknDocument
 {
 public:
 	CSdlAppDocument(CEikApplication& aApp) :
 	CEikAppUi* CreateAppUiL();
 };
 
-class CSdlAppUi : public CAknAppUi, public MExitWait
+class CSdlAppUi: public CAknAppUi, public MExitWait
 {
 public:
 	void ConstructL();
 void CExitWait::DoCancel()
 {
 	if (iStatusPtr != NULL)
+	{
 		User::RequestComplete(iStatusPtr, KErrCancel);
+	}
 }
 
 void CExitWait::Start()
 
 void CSDLWin::Draw(const TRect& /*aRect*/) const
 {
+	// Be transparent until app is ready. I don't want to force anyone use black initial screen.
+#if(0)	
 	CWindowGc& gc = SystemGc();
-	gc.SetPenStyle(CGraphicsContext::ESolidPen);
+	gc.SetDrawMode( CGraphicsContext::EDrawModeWriteAlpha );
+	gc.Clear();
+	
+	gc.SetPenStyle(CGraphicsContext::ENullPen);
 	gc.SetPenColor(0x000000);
 	gc.SetBrushStyle(CGraphicsContext::ESolidBrush);
-	gc.SetBrushColor(0x000000);
+	gc.SetBrushColor(0x80010101);
 	gc.DrawRect(Rect());
-	 
+#endif
 }
+/*
+ TKeyResponse CSDLWin::OfferKeyEventL(const TKeyEvent &aKeyEvent, TEventCode aType)
+ {
+ TKeyResponse result = EKeyWasNotConsumed;
+ if ( aKeyEvent.iScanCode == 164 )
+ {		
+ result = EKeyWasConsumed;
+ }
+ return result;
+ }
+ */
 
 void CSdlAppUi::ConstructL()
 {
 	iSDLWin->ConstructL(ApplicationRect());
 
 	iWait = new (ELeave) CExitWait(*this);
-	
+
 	StartTestL(0);
 }
 
 void CSdlAppUi::HandleCommandL(TInt aCommand)
 {
-	
-	switch(aCommand)
-	{
-		//case EAknCmdExit:
-		case EAknSoftkeyExit:
-		//case EEikCmdExit:
-		Done = 1;
-		iExit = ETrue;
-		if(iWait == NULL || !iWait->IsActive())
-		Exit();
-		
-		break; 
-	}
-	
-	
-	//if(iSdl == NULL)
-	//	StartTestL(aCommand);
+LOGMAN_SENDLOGF("HandleCommandL:%d", aCommand)
+#if(0)
+switch (aCommand)
+{
+	//case EAknCmdExit:
+	case EAknSoftkeyExit:
+	//case EEikCmdExit:
+	Done = 1;
+	iExit = ETrue;
+	if (iWait == NULL || !iWait->IsActive())
+	Exit();
+
+	break;
+}
+
+if(iSdl == NULL)
+StartTestL(aCommand);
+#endif	
 }
 
 void CSdlAppUi::StartTestL(TInt aCmd)
 {
-	TInt flags = CSDL::EDrawModeDSBDoubleBuffer;;
- 
+
+	//TInt flags = CSDL::EDrawModeGdi | CSDL::EEnableFocusStop
+	//		| CSDL::EMainThread;// | CSDL::EAutoOrientation;
+		
+	TInt flags = 0;
+	// This seems to be required to support pygame launcher.
+	flags |= CSDL::EDrawModeDSBDoubleBuffer;
+	// Don't draw when in background. 
+	flags |= CSDL::EEnableFocusStop;
+	// This should be on by default anyway
+	flags |= CSDL::EMainThread;
+	
 	iSdl = CSDL::NewL(flags);
 
-	iSdl->SetContainerWindowL(iSDLWin->GetWindow(), 
-	iEikonEnv->WsSession(), *iEikonEnv->ScreenDevice());	
+	iSdl->SetContainerWindowL(iSDLWin->GetWindow(), iEikonEnv->WsSession(),
+			*iEikonEnv->ScreenDevice());
 	iSdl->CallMainL(iWait->iStatus);
 	iWait->Start();
 }
- 
+
 void CSdlAppUi::DoExit(TInt aErr)
 {
 	if (aErr != KErrNone)
 	{
 		CAknErrorNote* err = new (ELeave) CAknErrorNote(ETrue);
 		TBuf<64> buf;
-		buf.Format(_L("SDL Error %d"), aErr);
+		if (aErr == 1)
+		{
+			buf.Copy(_L("Python run-time error."));
+		}
+		else
+		{
+			buf.Format(_L("SDL Error %d"), aErr);
+		}
 		err->ExecuteLD(buf);
 	}
 	else
 	{
 		/*
-		CAknInformationNote* info = new (ELeave) CAknInformationNote(ETrue);
-		info->SetTimeout(CAknNoteDialog::ENoTimeout);
-		TBuf<64> buf;
-		const TReal ticks = TReal(Ticks) / 1000.0;
-		const TReal fps = TReal(Frames) / ticks;
-		buf.Format(_L("Fps %f, %dms %d frames"), fps, Ticks, Frames);
-		info->ExecuteLD(buf);
-		*/
+		 CAknInformationNote* info = new (ELeave) CAknInformationNote(ETrue);
+		 info->SetTimeout(CAknNoteDialog::ENoTimeout);
+		 TBuf<64> buf;
+		 const TReal ticks = TReal(Ticks) / 1000.0;
+		 const TReal fps = TReal(Frames) / ticks;
+		 buf.Format(_L("Fps %f, %dms %d frames"), fps, Ticks, Frames);
+		 info->ExecuteLD(buf);
+		 */
 	}
 	delete iSdl;
 	iSdl = NULL;
 
 	// Exits after main script has completed
-	Exit(); 
+	Exit();
 }
 
-void CSdlAppUi::HandleWsEventL(const TWsEvent& aEvent, CCoeControl* aDestination)
+void CSdlAppUi::HandleWsEventL(const TWsEvent& aEvent,
+		CCoeControl* aDestination)
 {
 	if (iSdl != NULL)
 		iSdl->AppendWsEvent(aEvent);
 
 CEikAppUi* CSdlAppDocument::CreateAppUiL()
 {
-	return new(ELeave) CSdlAppUi();
+	return new (ELeave) CSdlAppUi();
 }
 
 TUid CSdlApplication::AppDllUid() const
 
 GLDEF_C TInt E32Main()
 {
-// TODO: Is this the only way to set heap size on emulator?
+	// TODO: Is this the only way to set heap size on emulator?
 	//#ifdef __WINS__	
-	RHeap *heap = UserHeap::ChunkHeap(0,100000,10000000,100000);
+	RHeap *heap = UserHeap::ChunkHeap(0, 100000, 10000000, 100000);
 	User::SwitchHeap(heap);
 	//#endif	
 	TInt result = EikStart::RunApplication(NewApplication);

File symbian/app/pygame_main.cpp

+#include <sdl.h>
+#include <Python.h>
+#include <CSPyInterpreter.h>
+
+#include "logmanutils.h"
+
+extern int Frames;
+extern int Ticks;
+extern int Done;
+
+void panic(char* aWhen)
+{
+	fprintf(stderr, "SDL error: %s: %s\n", aWhen, SDL_GetError());
+	SDL_Delay(1000);
+	SDL_Quit();
+	exit(-1);
+}
+
+extern "C"
+struct _inittab _PyGame_Inittab[];
+
+#define NL "\n"
+
+char* PYGAME_MAIN_SCRIPT_PATH[1] = {"\\data\\pygame\\pygame_main.py"};
+
+int main(int argc, char** argv)
+{
+	// Execute the main script
+
+	CSPyInterpreter* interp = CSPyInterpreter::NewInterpreterL();
+
+	// Add built-in pygame modules
+	PyImport_ExtendInittab(_PyGame_Inittab);
+			
+	LOGMAN_SENDLOG( "Entering interpreter");
+	TInt result = interp->RunScript(1, PYGAME_MAIN_SCRIPT_PATH);
+	LOGMAN_SENDLOGF( "Interpreter result:%d", result )
+	
+	PyEval_RestoreThread(PYTHON_TLS->thread_state);
+	Py_Finalize();
+	
+	delete interp;
+
+	SDL_Quit();
+	return result;
+}

File symbian/app/pygame_main.py

-# Quick and dirty demo
+"""
+pygame main script for S60
+"""
+import os
+import sys
 
-import sys
-import os
-
-BLACK = 0, 0, 0
-DISPLAY_SIZE = 240, 320
 
 if os.name == "e32":
     f=open('/data/pygame/stdout.txt','w')
-    f.write('SDL!\n')
     sys.stdout = f
     sys.stderr = f
-    
-    THISDIR = "\\data\\pygame"
-    
-else:
-    THISDIR = os.path.dirname( __file__ )
 
-import pygame
 
-surfaces = []
+__file__ = sys.argv[0]
+THISDIR = os.path.dirname( __file__ )
+sys.path.append( os.path.join( THISDIR, "libs") )
 
-def fade( screen, clock, image, imgrect, steps = 15, fadein = True ):            
-    # Center the image        
-    jump = 256. / steps
-     
-    for x in xrange( 1, ( steps + 1) ):
-        x = x*jump
-        if not fadein:
-            x = 256 - x
-        
-        clock.tick(30)
-        image.set_alpha( x )
-        
-        screen.fill(BLACK)
-        for img, rect in surfaces:
-            screen.blit(img, rect )
-        pygame.display.flip()
-        
-    return imgrect
-    
-def imagefade(screen, clock, image, posy, delay = 100, fadeout = True ):
-    
-    imgrect = image.get_rect()            
-    surfaces.append( ( image, imgrect ) )
-    
-    posx = pygame.display.Info().current_w / 2 - imgrect.w / 2
-    posy = posy - imgrect.h / 2
-    #posy = pygame.display.Info().current_h / 2 - imgrect.h / 2
-    
-    imgrect.x = posx
-    imgrect.y = posy
-    
-    imgrect = fade( screen, clock, image, imgrect  )
-    
-    pygame.time.delay(delay)
-    
-    if fadeout:
-        imgrect = fade( screen, clock, image, imgrect, fadein = False )
-    
-    return image, imgrect
+path_to_app = os.path.join( THISDIR, "launcher", "pygame_launcher.py" )
+execfile(path_to_app, {'__builtins__': __builtins__,
+                   '__name__': '__main__',
+                   '__file__': path_to_app } )
 
-def show_msg( screen, clock, text ):
-    
-    font = pygame.font.Font(None, 20)
-    size = font.size(text)
-    print "size", size
-    fg = 0, 200, 0
-    bg = 5, 5, 5
-    
-    ren = font.render(text, 0, fg, bg)  
-    
-    rect = ren.get_rect()    
-        
-    steps = 30
-    jump = 256. / steps
-    
-    angle_jump = 90. / steps * 2
-    
-    for x in xrange( 1, ( steps + 1) ):
-        if x < steps / 2:
-            img  = pygame.transform.rotate( ren, 90 - angle_jump * x )        
-        else:
-            img = ren
-        
-        x = x*jump        
-        x = 256 - x
-        clock.tick(30)
-        
-        screen.fill(BLACK)
-        
-        
-        img.set_alpha( x )
-        
-        rect = img.get_rect()
-        posx = pygame.display.Info().current_w / 2 - rect.w / 2
-        posy = pygame.display.Info().current_h / 2 - rect.h / 2
-        
-        rect.x = posx
-        rect.y = posy
-    
-        screen.blit(img, rect )
-        
-        pygame.display.flip()
-     
-        
-def intro():
-    
-    pygame.display.init()    
-    pygame.font.init()
-    
-    print pygame.display.list_modes()
-    size = width, height = pygame.display.list_modes()[0]
-    screen = None
-    if os.name == "e32":
-        try:
-            screen = pygame.display.set_mode(size)
-        except pygame.error:
-            screen = pygame.display.set_mode( (32,32) )#DISPLAY_SIZE)
-    else:
-        screen = pygame.display.set_mode( DISPLAY_SIZE )
-        
-    clock = pygame.time.Clock()    
-    
-    global show_msg
-    text = 'Pygame Demo loading'
-    show_msg( screen, clock, text )
-    
-    pygame.mixer.init( 22050, -32, 0, 4094)    
-    
-    # Init the rest
-    pygame.init()       
-    
-    show_msg = False
-    
-    img_sdl    = pygame.image.load( os.path.join( THISDIR, "sdl_powered.bmp") )
-    img_python = pygame.image.load( os.path.join( THISDIR, "python_powered.bmp") )
-    img_pygame = pygame.image.load( os.path.join( THISDIR, "pygame_powered.bmp") )
-    
-    s1 = pygame.mixer.Sound( THISDIR + "\\ambient.ogg" )
-    #s2 = pygame.mixer.Sound( THISDIR + "\\piano.ogg" )
-    
-    s1.set_volume( 0.1 )
-    #s2.set_volume( 0.1 )
-        
-    speed = [2, 2]
-        
-    #s2.play()
-    posy = pygame.display.Info().current_h / 9
-    s1.play()
-    imagefade( screen, clock, img_sdl, posy * 2, fadeout=False )
-    imagefade( screen, clock, img_python, posy * 6, fadeout=False )
-    imagefade( screen, clock, img_pygame, posy * 4, fadeout=False )
-     
-    global surfaces
-    pygame_img = surfaces[-1]
-    surfaces = surfaces[:-1]
-    
-    steps = 15
-    jump = 256. / steps     
-    for x in xrange( 1, ( steps + 1) ):
-        x = x*jump        
-        x = 256 - x
-        
-        clock.tick(30)
-        for event in pygame.event.get():
-            if event.type == pygame.KEYDOWN:
-                if event.scancode == 165:
-                    running = 0
-                elif event.key == pygame.constants.K_ESCAPE:
-                    running = 0
-            elif event.type == pygame.QUIT:
-                running = 0
-            print event
-                        
-        screen.fill(BLACK)
-        
-        img, rect = pygame_img        
-        screen.blit(img, rect ) 
-        
-        for img, rect in surfaces:
-            img.set_alpha( x )
-            screen.blit(img, rect )
-        pygame.display.flip()
-    
-    s1.fadeout(1500)
-    #s2.fadeout(500)
-    pygame.time.delay( 1500 )
-    
-
-    steps = 15
-    jump = 256. / steps
-    for i in xrange( 1, ( steps + 1) ):
-        x = i*jump        
-        x = 256 - x
-        
-        clock.tick(30)
-        for event in pygame.event.get():
-            if event.type == pygame.KEYDOWN:
-                if event.scancode == 165:
-                    running = 0
-                elif event.key == pygame.constants.K_ESCAPE:
-                    running = 0
-            elif event.type == pygame.QUIT:
-                running = 0
-                        
-        screen.fill(BLACK)
-        img, rect = pygame_img
-        
-        scale = 1.0 - i / float(steps)       
-        im  = pygame.transform.scale( img, (rect.w * scale, rect.h * scale) )       
-        im.set_alpha( x )
-        screen.blit(im, rect )
-        pygame.display.flip()
-    
-    pygame.time.delay( 500 )
-    
-intro()
-
-if os.name != "nt":
-    f.close()
-
+if os.name == "e32":
+    sys.stdout.flush()
+    sys.stdout.close()

File symbian/how_to_build.txt

 Basic requirements:
 - S60 3rd or 5th ed. SDK 
 - SVN client
-
+- Python 2.5.x
 
 === Getting necessary libraries ===
 
 
 The pygame can be built against the official(or any) pys60 version. 
 Initialize the release script by giving it information about the python dll to be linked with
-and the sis file to be embedded with it's UID.
+and the sis file to be embedded.( UID is read directly from the sis package )
 - TODO: Not implemented yet!!
-- configure.py pythondll=python222 pys60-embed=Python_1.4.5_3rdEd.sis
-- configure.py pythondll=python25  pys60-embed=Python_1.9.0_3rdEd.sis
+- configure.py pythondll=python222 pys60_sis=Python_1.4.5_3rdEd.sis
+- configure.py pythondll=python25  pys60_sis=Python_1.9.0_3rdEd.sis
 
 or
 
 - The above parameters are handled automatically in that case.
 - Of course, you can build the PyS60 CE manually and configure the build script as above.
 
-# Build everything, including pys60
+# Build everything, including PyS60 CE
 configure.py pys60_ce_src=\projects\pys60ce\trunk\src
 
 # Use existing python sis. Need to configure the python dll for linker.
 - Run: BuildPygameRelease.py 
 
 Device:
-- Run: BuildPygameRelease.py compiler=gcce release=urel cert=mycert.cer privkey=mycert.key passphprase=mypassword gcce_options="-O2 -fno-unit-at-a-time"
--- NOTE: The gcce_options are optional but recommended to reduce the binary size.
+- Run: BuildPygameRelease.py compiler=gcce release=urel
+-- NOTE: The you can set your certificate information with configure.py
 
 
 === Misc info ===
 pygame library
 - The pygame is implemented as static library to be statically linked to pygame application.
 
-About selecting Python:
-- You can select the python.dll to link against by giving scons parameter 'pythondll' 
-- ex. scons pythondll=Python222
-- default is "pygame_python"