Archive for June, 2011

Preview Android Boot Animations

June 17, 2011

I was looking for a new android boot animation after flashing a new ROM to my Evo Shift. There are a lot of animations posted in the xda forums, but unfortunately not all of them come with screenshots, and even the ones that do are usually static.

I did a quick search for a way to preview them on my computer and found a program on xda forums for just that. Unfortunately it was written in .NET so it’s not cross platform and can’t be used in linux.

In the end, I decided to refresh my pygtk knowledge and write my own simple script for previewing boot animations, code below. It can be used by “boot_animation.py ” or just launching it and dragging a boot animation .zip file onto the window.

import zipfile
import gtk
import glib
import sys



class Screen( gtk.DrawingArea ):
    """ This class is a Drawing Area"""
    def __init__(self, fps):
        super(Screen,self).__init__()
        ## Old fashioned way to connect expose. I don't savvy the gobject stuff.
        self.connect ( "expose_event", self.do_expose_event )
        ## This is what gives the animation life!
        dt = int(1./fps * 1000.)
        glib.timeout_add( dt, self.tick ) # Go call tick every 50 whatsits.
        self._running = True

    def tick ( self ):
        ## This invalidates the screen, causing the expose event to fire.
        self.alloc = self.get_allocation ( )
        rect = gtk.gdk.Rectangle ( self.alloc.x, self.alloc.y, self.alloc.width, self.alloc.height )
        self.window.invalidate_rect ( rect, True )        
        return self._running # Causes timeout to tick again.

    ## When expose event fires, this is run
    def do_expose_event( self, widget, event ):
        self.cr = self.window.cairo_create( )
        ## Call our draw function to do stuff.
        self.draw( *self.window.get_size( ) )
        


class Ani(Screen):
    '''Animation widget for displaying a list of sequence of frames (pixbufs)'''
    def __init__(self, anim_list, fps, loop):
            
        self.images = anim_list
        self.loop = loop
        
        Screen.__init__(self, fps)

        # init the generator        
        self._iter = self.iter()
        
    def iter(self):
        '''A generator to dole out the animation frames in the right 'order' '''
        p = 0
        for pixbuf_list in self.images:
            p += 1
            self.parent.set_title('Running: sequence {0}'.format(p))
            for pixbuf in pixbuf_list:
                yield pixbuf
                
        while self.loop:
            # loop final list
            self.parent.set_title('Looping: sequence {0}'.format(p))
            for pixbuf in self.images[-1]:
                yield pixbuf
        
        # no looping
        self.parent.set_title('Stopped.')
        self._running = False
        while True:
            # but we may still get expose events
            yield self.images[-1][-1]
        
    def next(self):
        # return next image
        return self._iter.next()
    
    def draw(self, w, h):
        # draw image
        self.window.draw_pixbuf(self.get_style().fg_gc[gtk.STATE_NORMAL], self.next(), 0, 0, 0, 0)
        

class Main(gtk.Window):
    def __init__(self):
        super(Main, self).__init__()
        
        self.connect( "delete-event", gtk.main_quit )
        self.connect( "destroy", gtk.main_quit )
        self.widget = None
        self.set_size_request ( 400, 400 )


        ###### Drag n Drop Stuff ######
        self.TARGET_TYPE_URI_LIST = 80
        dnd_list = [ ( 'text/uri-list', 0, self.TARGET_TYPE_URI_LIST ) ]
        self.drag_dest_set( gtk.DEST_DEFAULT_MOTION |
                      gtk.DEST_DEFAULT_HIGHLIGHT | gtk.DEST_DEFAULT_DROP,
                      dnd_list, gtk.gdk.ACTION_COPY)    
        self.connect('drag-data-received', self.drag_data_received)


    def load_boot_animation(self, file):
        '''Load a boot animation .zip file, creating an animation widget
        and displaying it in the window.'''
        if not zipfile.is_zipfile(file):
            print 'invalid zip file', file
            raise KeyError('invalid zip file')

        zf = zipfile.ZipFile(file, 'r')
        try:
            zf.getinfo('desc.txt')
        except KeyError:
            print 'invalid zipfile, no desc.txt'
            raise
            
        # read the desc.txt file
        spec = zf.read('desc.txt')
        file_list = zf.namelist()
        w, h, fps = map(int, spec.split()[:3])
        anim_list = []
        loop = False

        # parse the lines containing directorys
        for line in spec.split('\n')[1:]:
            line = line.strip()
            if line == '':
                # ignore blank lines
                continue

            # load images in the directory
            dir = line.split()[3]
            names = [x for x in file_list if dir in x]
            pixbufs = load_images(zf, names)
            anim_list.append(pixbufs)
            
            if line.split()[1] == '0': 
                # if this line is set to loop, nothing after will get displayed
                loop = True
                break
        

        # clear old animation
        if self.widget is not None:
            import gc
            kid = self.widget
            self.widget = None
            kid._running = False
            self.remove(kid)
            del kid.images # make sure to remove references to loaded images
            kid.destroy()
            del kid
            #gc.collect() # force freeing of image memory
            glib.idle_add(gc.collect)
            

        # create animation widget
        self.widget = Ani( anim_list, fps, loop )

        self.add(self.widget)
        self.show_all()
        self.set_size_request(w, h)
        

    def drag_data_received(self, wdg, context, x, y, selection, target_type, time):
        '''Event handler for dropping files onto the window.
        Contains a list of filenames, only keep the last one.'''
        if target_type == self.TARGET_TYPE_URI_LIST:
            uri = selection.data.strip().split('\n')[-1]
            file = uri_to_path(uri)

            try:
                # try to load it
                self.load_boot_animation(file)
            except KeyError:
                pass
                

def load_images(zf, names):
    '''Load a list of images from a ZipFile into pixbufs
        zf - ZipFile object containing images
        names - list of files to load 
    '''
    pixbufs = []
    for img in names:
        try:
            pbf = gtk.gdk.PixbufLoader()
            pbf.write(zf.read(img))
            pbf.close()
            pixbufs.append(pbf.get_pixbuf())
        except Exception, s:
            print 'cannot load {0}:'.format(img),s
    return pixbufs



def uri_to_path(uri):
    '''Convert a file URI to a path'''
    try:
        # gio is easier to use
        import gio
        path = gio.File(uri).get_path()
    except ImportError:
        # if gio isn't available
        import urllib
        # get the path to file
        path = ""
        if uri.startswith('file:\\\\\\'): # windows
	        path = uri[8:] # 8 is len('file:///')
        elif uri.startswith('file://'): # nautilus, rox
	        path = uri[7:] # 7 is len('file://')
        elif uri.startswith('file:'): # xffm
	        path = uri[5:] # 5 is len('file:')

        path = urllib.url2pathname(path) # escape special chars
        path = path.strip('\r\n\x00') # remove \r\n and NULL

    return path
	
	
def run():
    main_window = Main()
    
    if len(sys.argv) <= 1:
        print 'no arguments passed'
    else:
        file = sys.argv[1]

        if not zipfile.is_zipfile(file):
            print 'invalid file passed on cli', file
        else:
            main_window.load_boot_animation(file)

    # initial window display
    main_window.show_all()
    main_window.present( )

    # start mainloop
    gtk.main( )
    
if __name__ == '__main__':
    run()
Advertisements

Getting the serial PSC Powercan to work in Ubuntu

June 3, 2011

PSC Powerscan

I needed to get a PSC Powercan to work (scan a barcode and enter text) a little while ago, but it wasn’t being recognized as a keyboard device (the expected behavior). I did some searching, but most of the information I found applied only to barcode scanners that send ASCII over serial. Putting serio0 into serio_raw mode and hexdumping the data coming from the scanner, it was obvious that it was sending scancodes.

The solution I eventually found was a kernel parameter. The atkbd driver supports “dumb” keyboards, but apparently looks for a smart keyboard by default, putting the following on the kernel boot line made everything work fine: “atkbd.dumbkbd=1”

In ubuntu, adding it to /etc/default/grub makes it a permanent solution.