Author: duncan
Date: Sun Sep 23 15:40:08 2007
New Revision: 9891

Log:
Added updated main loop using kaa.notifier, supplied by dischi


Added:
   branches/rel-1/testing/Duncan/notifier/
   branches/rel-1/testing/Duncan/notifier/event.py
   branches/rel-1/testing/Duncan/notifier/main.py
   branches/rel-1/testing/Duncan/notifier/rc.py

Added: branches/rel-1/testing/Duncan/notifier/event.py
==============================================================================
--- (empty file)
+++ branches/rel-1/testing/Duncan/notifier/event.py     Sun Sep 23 15:40:08 2007
@@ -0,0 +1,649 @@
+# -*- coding: iso-8859-1 -*-
+# -----------------------------------------------------------------------
+# event.py - Global events for Freevo
+# -----------------------------------------------------------------------
+# $Id: event.py 9774 2007-07-27 19:09:32Z duncan $
+#
+# Notes:
+# Todo:
+#
+# -----------------------------------------------------------------------
+# Freevo - A Home Theater PC framework
+# Copyright (C) 2002 Krister Lagerstrom, et al.
+# Please see the file freevo/Docs/CREDITS for a complete list of authors.
+#
+# 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 2 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 MER-
+# CHANTABILITY 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, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# -----------------------------------------------------------------------
+
+import kaa.notifier
+
+class Event(kaa.notifier.Event):
+    """
+    an event is passed to the different eventhandlers in Freevo to
+    activate some action.
+    """
+    def __init__(self, name, arg=None, context=None, handler=None):
+        if isinstance(name, Event):
+            self.name    = name.name
+            self.arg     = name.arg
+            self.context = name.context
+            self.handler = name.handler
+        else:
+            self.name    = name
+            self.arg     = None
+            self.context = None
+            self.handler = None
+
+        if arg or arg == 0:
+            self.arg = arg
+
+        if context:
+            self.context = context
+
+        if handler:
+            self.handler = handler
+
+
+    def __str__(self):
+        """
+        return the event as string
+        """
+        return self.name
+
+
+    def __int__(self):
+        """
+        return the event as int (the last char of the name will be returned
+        as integer value
+        """
+        return int(self.name[-1])
+
+
+    def __cmp__(self, other):
+        """
+        compare function, return 0 if the objects are identical, 1 otherwise
+        """
+        if not other:
+            return 1
+        if isinstance(other, Event):
+            return self.name != other.name
+        return self.name != other
+
+
+
+
+#
+# Default actions Freevo knows
+#
+
+MIXER_VOLUP            = Event('MIXER_VOLUP', arg=5)
+MIXER_VOLDOWN          = Event('MIXER_VOLDOWN', arg=5)
+MIXER_MUTE             = Event('MIXER_MUTE')
+
+# To change the step size, but the following code in your
+# local_conf.py (setting VOL+ step size to 2)
+#
+# EVENTS['global']['VOL+'] = Event('MIXER_VOLUP', arg=2)
+
+
+PLAYLIST_NEXT          = Event('PLAYLIST_NEXT')
+PLAYLIST_PREV          = Event('PLAYLIST_PREV')
+PLAYLIST_TOGGLE_REPEAT = Event('PLAYLIST_TOGGLE_REPEAT')
+
+EJECT                  = Event('EJECT')
+
+#
+# Menu
+#
+
+MENU_LEFT              = Event('MENU_LEFT')
+MENU_RIGHT             = Event('MENU_RIGHT')
+MENU_UP                = Event('MENU_UP')
+MENU_DOWN              = Event('MENU_DOWN')
+MENU_PAGEUP            = Event('MENU_PAGEUP')
+MENU_PAGEDOWN          = Event('MENU_PAGEDOWN')
+MENU_REBUILD           = Event('MENU_REBUILD')
+
+MENU_GOTO_MAINMENU     = Event('MENU_GOTO_MAINMENU')
+MENU_GOTO_TV           = Event('MENU_GOTO_TV')
+MENU_GOTO_TVGUIDE      = Event('MENU_GOTO_TVGUIDE')
+MENU_GOTO_VIDEOS       = Event('MENU_GOTO_VIDEOS')
+MENU_GOTO_MUSIC        = Event('MENU_GOTO_MUSIC')
+MENU_GOTO_IMAGES       = Event('MENU_GOTO_IMAGES')
+MENU_GOTO_GAMES        = Event('MENU_GOTO_GAMES')
+MENU_GOTO_RADIO        = Event('MENU_GOTO_RADIO')
+MENU_GOTO_SHUTDOWN     = Event('MENU_GOTO_SHUTDOWN')
+MENU_BACK_ONE_MENU     = Event('MENU_BACK_ONE_MENU')
+
+MENU_SELECT            = Event('MENU_SELECT')
+MENU_PLAY_ITEM         = Event('MENU_PLAY_ITEM')
+MENU_SUBMENU           = Event('MENU_SUBMENU')
+MENU_CALL_ITEM_ACTION  = Event('MENU_CALL_ITEM_ACTION')
+MENU_CHANGE_STYLE      = Event('MENU_CHANGE_STYLE')
+
+
+DIRECTORY_CHANGE_DISPLAY_TYPE = Event('DIRECTORY_CHANGE_DISPLAY_TYPE')
+
+#
+# TV module
+#
+
+TV_START_RECORDING     = Event('TV_START_RECORDING')
+TV_CHANNEL_UP          = Event('TV_CHANNEL_UP')
+TV_CHANNEL_DOWN        = Event('TV_CHANNEL_DOWN')
+TV_CHANNEL_LAST        = Event('TV_CHANNEL_LAST')
+TV_SEND_TVTIME_CMD     = Event('TV_SEND_TVTIME_CMD')
+
+#
+# Global playing events
+#
+
+SEEK                   = Event('SEEK')
+PLAY                   = Event('PLAY')
+PAUSE                  = Event('PAUSE')
+STOP                   = Event('STOP')
+TOGGLE_OSD             = Event('TOGGLE_OSD')
+
+#
+# Video module
+#
+
+VIDEO_SEND_MPLAYER_CMD = Event('VIDEO_SEND_MPLAYER_CMD')
+VIDEO_SEND_XINE_CMD    = Event('VIDEO_SEND_XINE_CMD')
+VIDEO_MANUAL_SEEK      = Event('VIDEO_MANUAL_SEEK')
+VIDEO_NEXT_AUDIOLANG   = Event('VIDEO_NEXT_AUDIOLANG')
+VIDEO_NEXT_SUBTITLE    = Event('VIDEO_NEXT_SUBTITLE')
+VIDEO_TOGGLE_INTERLACE = Event('VIDEO_TOGGLE_INTERLACE')
+VIDEO_NEXT_ANGLE       = Event('VIDEO_NEXT_ANGLE')
+VIDEO_AVSYNC           = Event('VIDEO_AVSYNC')
+STORE_BOOKMARK         = Event('STORE_BOOKMARK')
+MENU                   = Event('MENU')
+
+DVDNAV_LEFT            = Event('DVDNAV_LEFT')
+DVDNAV_RIGHT           = Event('DVDNAV_RIGHT')
+DVDNAV_UP              = Event('DVDNAV_UP')
+DVDNAV_DOWN            = Event('DVDNAV_DOWN')
+DVDNAV_SELECT          = Event('DVDNAV_SELECT')
+DVDNAV_TITLEMENU       = Event('DVDNAV_TITLEMENU')
+DVDNAV_MENU            = Event('DVDNAV_MENU')
+NEXT                   = Event('NEXT')
+PREV                   = Event('PREV')
+
+
+#
+# Audio module
+#
+
+AUDIO_SEND_MPLAYER_CMD = Event('AUDIO_SEND_MPLAYER_CMD')
+AUDIO_LOG              = Event('AUDIO_LOG')
+
+
+#
+# Image module
+#
+
+IMAGE_ZOOM_GRID1       = Event('IMAGE_ZOOM_GRID1')
+IMAGE_ZOOM_GRID2       = Event('IMAGE_ZOOM_GRID2')
+IMAGE_ZOOM_GRID3       = Event('IMAGE_ZOOM_GRID3')
+IMAGE_ZOOM_GRID4       = Event('IMAGE_ZOOM_GRID4')
+IMAGE_ZOOM_GRID5       = Event('IMAGE_ZOOM_GRID5')
+IMAGE_ZOOM_GRID6       = Event('IMAGE_ZOOM_GRID6')
+IMAGE_ZOOM_GRID7       = Event('IMAGE_ZOOM_GRID7')
+IMAGE_ZOOM_GRID8       = Event('IMAGE_ZOOM_GRID8')
+IMAGE_ZOOM_GRID9       = Event('IMAGE_ZOOM_GRID9')
+
+IMAGE_NO_ZOOM          = Event('IMAGE_NO_ZOOM')
+
+IMAGE_ROTATE           = Event('IMAGE_ROTATE')
+IMAGE_SAVE             = Event('IMAGE_SAVE')
+
+IMAGE_MOVE             = Event('IMAGE_MOVE')
+IMAGE_TAG              = Event('IMAGE_TAG')
+
+#
+# Games module
+#
+
+GAMES_CONFIG           = Event('GAMES_CONFIG')
+GAMES_RESET            = Event('GAMES_RESET')
+GAMES_SNAPSHOT         = Event('GAMES_SNAPSHOT')
+
+
+#
+# Input boxes
+#
+
+INPUT_EXIT             = Event('INPUT_EXIT')
+INPUT_ENTER            = Event('INPUT_ENTER')
+INPUT_LEFT             = Event('INPUT_LEFT')
+INPUT_RIGHT            = Event('INPUT_RIGHT')
+INPUT_UP               = Event('INPUT_UP')
+INPUT_DOWN             = Event('INPUT_DOWN')
+INPUT_1                = Event('INPUT_1', arg=1)
+INPUT_2                = Event('INPUT_2', arg=2)
+INPUT_3                = Event('INPUT_3', arg=3)
+INPUT_4                = Event('INPUT_4', arg=4)
+INPUT_5                = Event('INPUT_5', arg=5)
+INPUT_6                = Event('INPUT_6', arg=6)
+INPUT_7                = Event('INPUT_7', arg=7)
+INPUT_8                = Event('INPUT_8', arg=8)
+INPUT_9                = Event('INPUT_9', arg=9)
+INPUT_0                = Event('INPUT_0', arg=0)
+
+INPUT_ALL_NUMBERS = (INPUT_0, INPUT_1, INPUT_2, INPUT_3, INPUT_4, INPUT_5,
+                     INPUT_6, INPUT_7, INPUT_8, INPUT_9, INPUT_0 )
+
+
+# Call the function specified in event.arg
+FUNCTION_CALL          = Event('FUNCTION_CALL')
+
+# All buttons which are not mapped to an event will be send as
+# BOTTON event with the pressed button as arg
+BUTTON                 = Event('BUTTON')
+RATING                 = Event('RATING')
+
+
+
+#
+# Default key-event map
+#
+
+MENU_EVENTS = {
+    'LEFT'      : MENU_LEFT,
+    'RIGHT'     : MENU_RIGHT,
+    'UP'        : MENU_UP,
+    'DOWN'      : MENU_DOWN,
+    'CH+'       : MENU_PAGEUP,
+    'CH-'       : MENU_PAGEDOWN,
+    'MENU'      : MENU_GOTO_MAINMENU,
+    'SHUTDOWN'  : MENU_GOTO_SHUTDOWN,
+    'EXIT'      : MENU_BACK_ONE_MENU,
+    'SELECT'    : MENU_SELECT,
+    'PLAY'      : MENU_PLAY_ITEM,
+    'ENTER'     : MENU_SUBMENU,
+    'DISPLAY'   : MENU_CHANGE_STYLE,
+    'EJECT'     : EJECT
+    }
+
+TVMENU_EVENTS = {
+    'LEFT'      : MENU_LEFT,
+    'RIGHT'     : MENU_RIGHT,
+    'UP'        : MENU_UP,
+    'DOWN'      : MENU_DOWN,
+    'CH+'       : MENU_PAGEUP,
+    'CH-'       : MENU_PAGEDOWN,
+    'MENU'      : MENU_GOTO_MAINMENU,
+    'SHUTDOWN'  : MENU_GOTO_SHUTDOWN,
+    'EXIT'      : MENU_BACK_ONE_MENU,
+    'SELECT'    : MENU_SELECT,
+    'ENTER'     : MENU_SUBMENU,
+    'DISPLAY'   : MENU_CHANGE_STYLE,
+    'PLAY'      : PLAY,
+    'REC'       : TV_START_RECORDING
+    }
+
+INPUT_EVENTS = {
+    'EXIT'      : INPUT_EXIT,
+    'ENTER'     : INPUT_ENTER,
+    'SELECT'    : INPUT_ENTER,
+    'LEFT'      : INPUT_LEFT,
+    'RIGHT'     : INPUT_RIGHT,
+    'UP'        : INPUT_UP,
+    'DOWN'      : INPUT_DOWN,
+    '1'         : INPUT_1,
+    '2'         : INPUT_2,
+    '3'         : INPUT_3,
+    '4'         : INPUT_4,
+    '5'         : INPUT_5,
+    '6'         : INPUT_6,
+    '7'         : INPUT_7,
+    '8'         : INPUT_8,
+    '9'         : INPUT_9,
+    '0'         : INPUT_0,
+    'CH+'       : MENU_PAGEUP,
+    'CH-'       : MENU_PAGEDOWN
+    }
+
+TV_EVENTS = {
+    'STOP'      : STOP,
+    'MENU'      : STOP,
+    'EXIT'      : STOP,
+    'SELECT'    : STOP,
+    'PAUSE'     : PAUSE,
+    'CH+'       : TV_CHANNEL_UP,
+    'CH-'       : TV_CHANNEL_DOWN,
+    'PREV_CH'   : TV_CHANNEL_LAST,
+    'LEFT'      : Event(SEEK, arg=-60),
+    'RIGHT'     : Event(SEEK, arg=60),
+    'REW'       : Event(SEEK, arg=-10),
+    'FFWD'      : Event(SEEK, arg=10),
+    'DISPLAY'   : TOGGLE_OSD,
+    '0'         : INPUT_0,
+    '1'         : INPUT_1,
+    '2'         : INPUT_2,
+    '3'         : INPUT_3,
+    '4'         : INPUT_4,
+    '5'         : INPUT_5,
+    '6'         : INPUT_6,
+    '7'         : INPUT_7,
+    '8'         : INPUT_8,
+    '9'         : INPUT_9,
+    }
+
+VIDEO_EVENTS = {
+    'PLAY'      : PLAY,
+    'PAUSE'     : PAUSE,
+    'STOP'      : STOP,
+    'EXIT'      : STOP,
+    'UP'        : PLAYLIST_PREV,
+    'DOWN'      : PLAYLIST_NEXT,
+    'CH+'       : PLAYLIST_PREV,
+    'CH-'       : PLAYLIST_NEXT,
+    'LEFT'      : Event(SEEK, arg=-60),
+    'RIGHT'     : Event(SEEK, arg=60),
+    'REW'       : Event(SEEK, arg=-10),
+    'FFWD'      : Event(SEEK, arg=10),
+    'MENU'      : MENU,
+    'DISPLAY'   : TOGGLE_OSD,
+    'REC'       : STORE_BOOKMARK,
+    '0'         : VIDEO_MANUAL_SEEK,
+    'AVSYNC+'   : Event(VIDEO_AVSYNC, arg=0.100),
+    'AVSYNC-'   : Event(VIDEO_AVSYNC, arg=-0.100),
+    }
+
+DVD_EVENTS = {
+    'PLAY'      : PLAY,
+    'PAUSE'     : PAUSE,
+    'STOP'      : STOP,
+    'EXIT'      : STOP,
+    'UP'        : DVDNAV_UP,
+    'DOWN'      : DVDNAV_DOWN,
+    'LEFT'      : DVDNAV_LEFT,
+    'RIGHT'     : DVDNAV_RIGHT,
+    'ENTER'     : DVDNAV_SELECT,
+    'SELECT'    : DVDNAV_SELECT,
+    'DISPLAY'   : TOGGLE_OSD,
+    'REW'       : Event(SEEK, arg=-10),
+    'FFWD'      : Event(SEEK, arg=10),
+    'GUIDE'     : DVDNAV_TITLEMENU,
+    'MENU'      : DVDNAV_MENU,
+    'LANG'      : VIDEO_NEXT_AUDIOLANG,
+    'SUBTITLE'  : VIDEO_NEXT_SUBTITLE,
+    'ANGLE'     : VIDEO_NEXT_ANGLE,
+    'CH+'       : NEXT,
+    'CH-'       : PREV
+    }
+
+VCD_EVENTS = {
+    'PLAY'      : PLAY,
+    'PAUSE'     : PAUSE,
+    'STOP'      : STOP,
+    'EXIT'      : STOP,
+    'LEFT'      : Event(SEEK, arg=-60),
+    'RIGHT'     : Event(SEEK, arg=60),
+    'REW'       : Event(SEEK, arg=-10),
+    'FFWD'      : Event(SEEK, arg=10),
+    'MENU'      : MENU,
+    'DISPLAY'   : TOGGLE_OSD,
+    'LANG'      : VIDEO_NEXT_AUDIOLANG,
+    'SUBTITLE'  : VIDEO_NEXT_SUBTITLE,
+    'ANGLE'     : VIDEO_NEXT_ANGLE,
+    '1'         : INPUT_1,
+    '2'         : INPUT_2,
+    '3'         : INPUT_3,
+    '4'         : INPUT_4,
+    '5'         : INPUT_5,
+    '6'         : INPUT_6,
+    '7'         : INPUT_7,
+    '8'         : INPUT_8,
+    '9'         : INPUT_9
+    }
+
+AUDIO_EVENTS = {
+    'STOP'      : STOP,
+    'EXIT'      : STOP,
+    'PLAY'      : PLAY,
+    'PAUSE'     : PAUSE,
+    'LEFT'      : Event(SEEK, arg=-60),
+    'RIGHT'     : Event(SEEK, arg=60),
+    'REW'       : Event(SEEK, arg=-10),
+    'FFWD'      : Event(SEEK, arg=10),
+    'UP'        : PLAYLIST_PREV,
+    'DOWN'      : PLAYLIST_NEXT,
+    'CH+'       : PLAYLIST_PREV,
+    'CH-'       : PLAYLIST_NEXT,
+    '1'         : INPUT_1,
+    '2'         : INPUT_2,
+    '3'         : INPUT_3,
+    '4'         : INPUT_4,
+    '5'         : INPUT_5,
+    '6'         : INPUT_6,
+    '7'         : INPUT_7,
+    '8'         : INPUT_8,
+    '9'         : INPUT_9,
+    }
+
+IMAGE_EVENTS = {
+    'STOP'      : STOP,
+    'EXIT'      : STOP,
+    'PLAY'      : PLAY,
+    'PAUSE'     : PAUSE,
+    'LEFT'      : Event(IMAGE_ROTATE, arg='left'),
+    'RIGHT'     : Event(IMAGE_ROTATE, arg='right'),
+    '1'         : IMAGE_ZOOM_GRID1,
+    '2'         : IMAGE_ZOOM_GRID2,
+    '3'         : IMAGE_ZOOM_GRID3,
+    '4'         : IMAGE_ZOOM_GRID4,
+    '5'         : IMAGE_ZOOM_GRID5,
+    '6'         : IMAGE_ZOOM_GRID6,
+    '7'         : IMAGE_ZOOM_GRID7,
+    '8'         : IMAGE_ZOOM_GRID8,
+    '9'         : IMAGE_ZOOM_GRID9,
+    '0'         : IMAGE_NO_ZOOM,
+    'DISPLAY'   : TOGGLE_OSD,
+    'REC'       : IMAGE_SAVE,
+    'ENTER'     : IMAGE_TAG,
+    'UP'        : PLAYLIST_PREV,
+    'DOWN'      : PLAYLIST_NEXT,
+    'CH+'       : PLAYLIST_PREV,
+    'CH-'       : PLAYLIST_NEXT
+    }
+
+IMAGE_ZOOM_EVENTS = {
+    'STOP'      : STOP,
+    'EXIT'      : STOP,
+    'PLAY'      : PLAY,
+    'PAUSE'     : PAUSE,
+    'LEFT'      : Event(IMAGE_MOVE, arg=(-60,  0)),
+    'RIGHT'     : Event(IMAGE_MOVE, arg=( 60,  0)),
+    'UP'        : Event(IMAGE_MOVE, arg=(  0,-60)),
+    'DOWN'      : Event(IMAGE_MOVE, arg=(  0, 60)),
+    '1'         : IMAGE_ZOOM_GRID1,
+    '2'         : IMAGE_ZOOM_GRID2,
+    '3'         : IMAGE_ZOOM_GRID3,
+    '4'         : IMAGE_ZOOM_GRID4,
+    '5'         : IMAGE_ZOOM_GRID5,
+    '6'         : IMAGE_ZOOM_GRID6,
+    '7'         : IMAGE_ZOOM_GRID7,
+    '8'         : IMAGE_ZOOM_GRID8,
+    '9'         : IMAGE_ZOOM_GRID9,
+    '0'         : IMAGE_NO_ZOOM,
+    'DISPLAY'   : TOGGLE_OSD,
+    'REC'       : IMAGE_SAVE,
+    'CH+'       : PLAYLIST_PREV,
+    'CH-'       : PLAYLIST_NEXT
+    }
+
+GAMES_EVENTS = {
+    'STOP'      : STOP,
+    'SELECT'    : STOP,
+    'MENU'      : MENU,
+    'DISPLAY'   : GAMES_CONFIG,
+    'ENTER'     : GAMES_RESET
+}
+
+GLOBAL_EVENTS = {
+    'VOL+'      : MIXER_VOLUP,
+    'VOL-'      : MIXER_VOLDOWN,
+    'MUTE'      : MIXER_MUTE
+    }
+
+
+import pygame.locals as key
+
+DEFAULT_KEYMAP = {
+    key.K_F1          : 'SLEEP',
+    key.K_HOME        : 'MENU',
+    key.K_g           : 'GUIDE',
+    key.K_ESCAPE      : 'EXIT',
+    key.K_UP          : 'UP',
+    key.K_DOWN        : 'DOWN',
+    key.K_LEFT        : 'LEFT',
+    key.K_RIGHT       : 'RIGHT',
+    key.K_SPACE       : 'SELECT',
+    key.K_RETURN      : 'SELECT',
+    key.K_F2          : 'POWER',
+    key.K_F3          : 'MUTE',
+    key.K_KP_MINUS    : 'VOL-',
+    key.K_n           : 'VOL-',
+    key.K_KP_PLUS     : 'VOL+',
+    key.K_m           : 'VOL+',
+    key.K_c           : 'CH+',
+    key.K_v           : 'CH-',
+    key.K_1           : '1',
+    key.K_2           : '2',
+    key.K_3           : '3',
+    key.K_4           : '4',
+    key.K_5           : '5',
+    key.K_6           : '6',
+    key.K_7           : '7',
+    key.K_8           : '8',
+    key.K_9           : '9',
+    key.K_0           : '0',
+    key.K_d           : 'DISPLAY',
+    key.K_e           : 'ENTER',
+    key.K_UNDERSCORE  : 'PREV_CH',
+    key.K_o           : 'PIP_ONOFF',
+    key.K_w           : 'PIP_SWAP',
+    key.K_i           : 'PIP_MOVE',
+    key.K_F4          : 'TV_VCR',
+    key.K_r           : 'REW',
+    key.K_p           : 'PLAY',
+    key.K_f           : 'FFWD',
+    key.K_u           : 'PAUSE',
+    key.K_s           : 'STOP',
+    key.K_F6          : 'REC',
+    key.K_PERIOD      : 'EJECT',
+    key.K_l           : 'SUBTITLE',
+    key.K_a           : 'LANG',
+    key.K_RIGHTBRACKET: 'NEXT',
+    key.K_LEFTBRACKET : 'PREV',
+    }
+
+
+DEFAULT_EVENTMAP = {
+    'KEY_F1'          : 'SLEEP',
+    'KEY_HOME'        : 'MENU',
+    'KEY_G'           : 'GUIDE',
+    'KEY_ESC'         : 'EXIT',
+    'KEY_UP'          : 'UP',
+    'KEY_DOWN'        : 'DOWN',
+    'KEY_LEFT'        : 'LEFT',
+    'KEY_RIGHT'       : 'RIGHT',
+    'KEY_OK'          : 'SELECT',
+    'KEY_SPACE'       : 'SELECT',
+    'KEY_ENTER'       : 'SELECT',
+    'KEY_KPENTER'     : 'SELECT',
+    'KEY_POWER'       : 'POWER',
+    'KEY_F2'          : 'POWER',
+    'KEY_MUTE'        : 'MUTE',
+    'KEY_F3'          : 'MUTE',
+    'KEY_VOLUMEDOWN'  : 'VOL-',
+    'KEY_KPMINUS'     : 'VOL-',
+    'KEY_N'           : 'VOL-',
+    'KEY_VOLUMEUP'    : 'VOL+',
+    'KEY_KPPLUS'      : 'VOL+',
+    'KEY_M'           : 'VOL+',
+    'KEY_CHANNELUP'   : 'CH+',
+    'KEY_C'           : 'CH+',
+    'KEY_CHANNELDOWN' : 'CH-',
+    'KEY_V'           : 'CH-',
+    'KEY_1'           : '1',
+    'KEY_2'           : '2',
+    'KEY_3'           : '3',
+    'KEY_4'           : '4',
+    'KEY_5'           : '5',
+    'KEY_6'           : '6',
+    'KEY_7'           : '7',
+    'KEY_8'           : '8',
+    'KEY_9'           : '9',
+    'KEY_0'           : '0',
+    'KEY_VENDOR'      : 'DISPLAY',
+    'KEY_D'           : 'DISPLAY',
+    'KEY_MENU'        : 'ENTER',
+    'KEY_E'           : 'ENTER',
+    'KEY_MINUS'       : 'PREV_CH',
+    'KEY_O'           : 'PIP_ONOFF',
+    'KEY_W'           : 'PIP_SWAP',
+    'KEY_I'           : 'PIP_MOVE',
+    'KEY_F4'          : 'TV_VCR',
+    'KEY_REWIND'      : 'REW',
+    'KEY_R'           : 'REW',
+    'KEY_PLAY'        : 'PLAY',
+    'KEY_P'           : 'PLAY',
+    'KEY_FORWARD'     : 'FFWD',
+    'KEY_F'           : 'FFWD',
+    'KEY_PAUSE'       : 'PAUSE',
+    'KEY_U'           : 'PAUSE',
+    'KEY_STOP'        : 'STOP',
+    'KEY_S'           : 'STOP',
+    'KEY_RECORD'      : 'RECORD',
+    'KEY_F6'          : 'REC',
+    'KEY_PERIOD'      : 'EJECT',
+    'KEY_L'           : 'SUBTITLE',
+    'KEY_A'           : 'LANG',
+
+    'REL_X'           : ('LEFT', 'RIGHT'),
+    'REL_Y'           : ('UP', 'DOWN'),
+
+    'BTN_LEFT'        : 'SELECT',
+    'BTN_RIGHT'       : 'EXIT',
+    }
+
+
+
+#
+# Internal events, don't map any button on them
+#
+
+PLAY_END         = Event('PLAY_END')
+USER_END         = Event('USER_END')
+DVD_PROTECTED    = Event('DVD_PROTECTED')
+PLAY_START       = Event('PLAY_START')
+
+OSD_MESSAGE      = Event('OSD_MESSAGE')
+
+VIDEO_START      = Event('VIDEO_START')
+VIDEO_END        = Event('VIDEO_END')
+
+OS_EVENT_POPEN2  = Event('OS_EVENT_POPEN2')
+OS_EVENT_WAITPID = Event('OS_EVENT_WAITPID')
+OS_EVENT_KILL    = Event('OS_EVENT_KILL')
+
+RECORD_START     = Event('RECORD_START')
+RECORD_STOP      = Event('RECORD_STOP')
+
+MENU_PROCESS_END = Event('MENU_PROCESS_END')

Added: branches/rel-1/testing/Duncan/notifier/main.py
==============================================================================
--- (empty file)
+++ branches/rel-1/testing/Duncan/notifier/main.py      Sun Sep 23 15:40:08 2007
@@ -0,0 +1,509 @@
+# -*- coding: iso-8859-1 -*-
+# -----------------------------------------------------------------------
+# main.py - This is the Freevo main application code
+# -----------------------------------------------------------------------
+# $Id: main.py 9862 2007-09-10 15:36:28Z duncan $
+#
+# Notes:
+# Todo:
+#
+# -----------------------------------------------------------------------
+# Freevo - A Home Theater PC framework
+# Copyright (C) 2002 Krister Lagerstrom, et al.
+# Please see the file freevo/Docs/CREDITS for a complete list of authors.
+#
+# 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 2 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 MER-
+# CHANTABILITY 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, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# -----------------------------------------------------------------------
+
+
+# Must do this here to make sure no os.system() calls generated by module init
+# code gets LD_PRELOADed
+import os
+os.environ['LD_PRELOAD'] = ''
+
+import sys, time
+import traceback
+import signal
+
+
+# i18n support
+
+# First load the xml module. It's not needed here but it will mess
+# up with the domain we set (set it from freevo 4Suite). By loading it
+# first, Freevo will override the 4Suite setting to freevo
+
+try:
+    from xml.utils import qp_xml
+    from xml.dom import minidom
+
+    # now load other modules to check if all requirements are installed
+    import pygame
+    import twisted
+    import Numeric
+
+    import config
+    import kaa.metadata as mmpython
+    import kaa.notifier
+    import kaa.imlib2 as Image
+
+
+except ImportError, i:
+    print 'Can\'t find all Python dependencies:'
+    print i
+    if str(i)[-7:] == 'Numeric':
+        print 'You need to recompile pygame after installing Numeric!'
+    print
+    print 'Not all requirements of Freevo are installed on your system.'
+    print 'Please check the INSTALL file for more information.'
+    print
+    sys.exit(0)
+
+
+# check if kaa.metadata is up to date to avoid bug reports
+# for already fixed bugs
+try:
+    v = 'unknown'
+    import kaa.metadata.version
+    if kaa.metadata.version.VERSION < 0.6:
+        v = kaa.metadata.version.VERSION
+        raise ImportError
+except ImportError:
+    print 'Error: Installed kaa.metadata version (%s) is too old.' % v
+    print 'Please update kaa.metadata to version 0.6 or higher or get it with 
subversion'
+    print 'svn export svn://svn.freevo.org/kaa/trunk/metadata kaa/metadata'
+    print
+    sys.exit(0)
+
+# check if kaa.imlib2 is up to date to avoid bug reports
+# for already fixed bugs
+try:
+    v = 'unknown'
+    import kaa.imlib2.version
+    if kaa.imlib2.version.VERSION < 0.1:
+        v = kaa.metadata.version.VERSION
+        raise ImportError
+except ImportError:
+    print 'Error: Installed kaa.imlib2 version (%s) is too old.' % v
+    print 'Please update kaa.imlib2 to version 0.1 or higher or get it with 
subversion'
+    print 'svn export svn://svn.freevo.org/kaa/trunk/imlib2 kaa/imlib2'
+    print
+    sys.exit(0)
+
+import util    # Various utilities
+import osd     # The OSD class, used to communicate with the OSD daemon
+import menu    # The menu widget class
+import skin    # The skin class
+import rc      # The RemoteControl class.
+
+from item import Item
+from event import *
+from plugins.shutdown import shutdown
+
+
+# Create the OSD object
+osd = osd.get_singleton()
+
+
+class SkinSelectItem(Item):
+    """
+    Item for the skin selector
+    """
+    def __init__(self, parent, name, image, skin):
+        Item.__init__(self, parent)
+        self.name  = name
+        self.image = image
+        self.skin  = skin
+
+    def actions(self):
+        return [ ( self.select, '' ) ]
+
+    def select(self, arg=None, menuw=None):
+        """
+        Load the new skin and rebuild the main menu
+        """
+        import plugin
+        skin.set_base_fxd(self.skin)
+        pos = menuw.menustack[0].choices.index(menuw.menustack[0].selected)
+
+        parent = menuw.menustack[0].choices[0].parent
+        menuw.menustack[0].choices = []
+        for p in plugin.get('mainmenu'):
+            menuw.menustack[0].choices += p.items(parent)
+
+        for i in menuw.menustack[0].choices:
+            i.is_mainmenu_item = True
+
+        menuw.menustack[0].selected = menuw.menustack[0].choices[pos]
+        menuw.back_one_menu()
+
+
+
+class MainMenu(Item):
+    """
+    this class handles the main menu
+    """
+    def getcmd(self):
+        """
+        Setup the main menu and handle events (remote control, etc)
+        """
+        import plugin
+        menuw = menu.MenuWidget()
+        items = []
+        for p in plugin.get('mainmenu'):
+            items += p.items(self)
+
+        for i in items:
+            i.is_mainmenu_item = True
+
+        mainmenu = menu.Menu(_('Freevo Main Menu'), items, item_types='main', 
umount_all = 1)
+        menuw.pushmenu(mainmenu)
+        osd.add_app(menuw)
+
+
+    def eventhandler(self, event=None, menuw=None, arg=None):
+        """
+        Automatically perform actions depending on the event, e.g. play DVD
+        """
+        # pressing DISPLAY on the main menu will open a skin selector
+        # (only for the new skin code)
+        if event == MENU_CHANGE_STYLE:
+            items = []
+            for name, image, skinfile in skin.get_skins():
+                items += [ SkinSelectItem(self, name, image, skinfile) ]
+
+            menuw.pushmenu(menu.Menu(_('Skin Selector'), items))
+            return True
+
+        # give the event to the next eventhandler in the list
+        return Item.eventhandler(self, event, menuw)
+
+
+
+class Splashscreen(skin.Area):
+    """
+    A simple splash screen for osd startup
+    """
+    def __init__(self, text):
+        skin.Area.__init__(self, 'content')
+
+        self.pos          = 0
+        self.bar_border   = skin.Rectange(bgcolor=0xff000000L, size=2)
+        self.bar_position = skin.Rectange(bgcolor=0xa0000000L)
+        self.text         = text
+
+
+    def update_content(self):
+        """
+        there is no content in this area
+        """
+        layout    = self.layout
+        area      = self.area_val
+        content   = self.calc_geometry(layout.content, copy_object=True)
+
+        self.write_text(self.text, content.font, content, height=-1, 
align_h='center')
+
+        pos = 0
+        x0, x1 = content.x, content.x + content.width
+        y = content.y + content.font.font.height + content.spacing
+        if self.pos:
+            pos = round(float((x1 - x0 - 4)) / (float(100) / self.pos))
+        self.drawroundbox(x0, y, x1-x0, 20, self.bar_border)
+        self.drawroundbox(x0+2, y+2, pos, 16, self.bar_position)
+
+
+    def progress(self, pos):
+        """
+        set the progress position and refresh the screen
+        """
+        self.pos = pos
+        skin.draw('splashscreen', None)
+
+
+
+class MainTread:
+    """
+    The main thread or loop of freevo
+    """
+    def __init__(self):
+        """
+        get the list of plugins wanting events
+        """
+        self.eventhandler_plugins  = []
+        self.eventlistener_plugins = []
+
+        for p in plugin.get('daemon_eventhandler'):
+            if hasattr(p, 'event_listener') and p.event_listener:
+                self.eventlistener_plugins.append(p)
+            else:
+                self.eventhandler_plugins.append(p)
+        kaa.notifier.EventHandler(self.eventhandler).register()
+        
+
+    def eventhandler(self, event):
+        """
+        event handling function for the main loop
+        """
+        if event == OS_EVENT_POPEN2:
+            _debug_('popen2 %s' % event.arg[1])
+            event.arg[0].child = util.popen3.Popen3(event.arg[1])
+            return
+
+        _debug_('handling event %s' % str(event), 2)
+
+        for p in self.eventlistener_plugins:
+            p.eventhandler(event=event)
+
+        if event == FUNCTION_CALL:
+            event.arg()
+
+        elif event.handler:
+            event.handler(event=event)
+
+        # Send events to either the current app or the menu handler
+        elif rc.app():
+            if not rc.app()(event):
+                for p in self.eventhandler_plugins:
+                    if p.eventhandler(event=event):
+                        break
+                else:
+                    _debug_('no eventhandler for event %s' % event, 2)
+
+        else:
+            app = osd.focused_app()
+            if app:
+                try:
+                    if config.TIME_DEBUG:
+                        t1 = time.clock()
+                    app.eventhandler(event)
+                    if config.TIME_DEBUG:
+                        print time.clock() - t1
+
+                except SystemExit:
+                    raise SystemExit
+
+                except:
+                    if config.FREEVO_EVENTHANDLER_SANDBOX:
+                        traceback.print_exc()
+                        from gui import ConfirmBox
+                        pop = ConfirmBox(text=_('Event \'%s\' 
crashed\n\nPlease take a ' \
+                                                'look at the logfile and 
report the bug to ' \
+                                                'the Freevo mailing list. The 
state of '\
+                                                'Freevo may be corrupt now and 
this error '\
+                                                'could cause more errors until 
you restart '\
+                                                'Freevo.\n\nLogfile: %s\n\n') 
% \
+                                         (event, sys.stdout.logfile),
+                                         
width=osd.width-(config.OSD_OVERSCAN_LEFT+config.OSD_OVERSCAN_RIGHT)-50,
+                                         handler=shutdown,
+                                         handler_message = _('shutting 
down...'))
+                        pop.b0.set_text(_('Shutdown'))
+                        pop.b0.toggle_selected()
+                        pop.b1.set_text(_('Continue'))
+                        pop.b1.toggle_selected()
+                        pop.show()
+                    else:
+                        raise
+            else:
+                _debug_('no target for events given')
+
+
+def signal_handler(sig, frame):
+    """
+    the signal handler to shut down freevo
+    """
+    if sig in (signal.SIGTERM, signal.SIGINT):
+        shutdown(exit=True)
+
+
+def tracefunc(frame, event, arg, _indent=[0]):
+    """
+    function to trace everything inside freevo for debugging
+    """
+    if event == 'call':
+        filename = frame.f_code.co_filename
+        funcname = frame.f_code.co_name
+        lineno = frame.f_code.co_firstlineno
+        if 'self' in frame.f_locals:
+            try:
+                classinst = frame.f_locals['self']
+                classname = repr(classinst).split()[0].split('(')[0][1:]
+                funcname = '%s.%s' % (classname, funcname)
+            except:
+                pass
+        here = '%s:%s:%s()' % (filename, lineno, funcname)
+        _indent[0] += 1
+        tracefd.write('%4s %s%s\n' % (_indent[0], ' ' * _indent[0], here))
+        tracefd.flush()
+    elif event == 'return':
+        _indent[0] -= 1
+
+    return tracefunc
+
+
+
+#
+# Freevo main function
+#
+
+# parse arguments
+if len(sys.argv) >= 2:
+
+    # force fullscreen mode
+    # deactivate screen blanking and set osd to fullscreen
+    if sys.argv[1] == '-force-fs':
+        os.system('xset -dpms s off')
+        config.START_FULLSCREEN_X = 1
+
+    # activate a trace function
+    if sys.argv[1] == '-trace':
+        tracefd = open(os.path.join(config.FREEVO_LOGDIR, 'trace.txt'), 'w')
+        sys.settrace(tracefunc)
+        config.DEBUG = 2
+
+    # create api doc for Freevo and move it to Docs/api
+    if sys.argv[1] == '-doc':
+        import pydoc
+        import re
+        for file in util.match_files_recursively('src/', ['py' ]):
+            # doesn't work for everything :-(
+            if file not in ( 'src/tv/record_server.py', ) and \
+                   file.find('src/www') == -1 and \
+                   file.find('src/helpers') == -1:
+                file = re.sub('/', '.', file)
+                try:
+                    pydoc.writedoc(file[4:-3])
+                except:
+                    pass
+        try:
+            os.mkdir('Docs/api')
+        except:
+            pass
+        for file in util.match_files('.', ['html', ]):
+            print 'moving %s' % file
+            os.rename(file, 'Docs/api/%s' % file)
+        print
+        print 'wrote api doc to \'Docs/api\''
+        shutdown(exit=True)
+
+
+try:
+    # signal handler
+    signal.signal(signal.SIGTERM, signal_handler)
+    signal.signal(signal.SIGINT, signal_handler)
+
+    # load the fxditem to make sure it's the first in the
+    # mimetypes list
+    import fxditem
+
+    # load all plugins
+    import plugin
+
+    # prepare the skin
+    skin.prepare()
+
+    try:
+        try:
+            import freevo.version as version
+            import freevo.revision as revision
+        except ImportError:
+            import version
+            import revision
+        v = '%s' % version.__version__
+        v = v.replace('-svn', ' r%s' % revision.__revision__)
+    except ImportError:
+        pass
+    # Fire up splashscreen and load the plugins
+    splash = Splashscreen(_('Starting Freevo-%s, please wait ...') % v)
+    skin.register('splashscreen', ('screen', splash))
+    plugin.init(splash.progress)
+    skin.delete('splashscreen')
+
+    # Fire up splashscreen and load the cache
+    if config.MEDIAINFO_USE_MEMORY == 2:
+        import util.mediainfo
+
+        splash = Splashscreen(_('Reading cache, please wait ...'))
+        skin.register('splashscreen', ('screen', splash))
+
+        cachefiles = []
+        for type in ('video', 'audio', 'image', 'games'):
+            if plugin.is_active(type):
+                n = 'config.%s_ITEMS' % type.upper()
+                x = eval(n)
+                for item in x:
+                    if os.path.isdir(item[1]):
+                        cachefiles += [ item[1] ] + 
util.get_subdirs_recursively(item[1])
+
+
+        cachefiles = util.unique(cachefiles)
+
+        for f in cachefiles:
+            splash.progress(int((float((cachefiles.index(f)+1)) / 
len(cachefiles)) * 100))
+            util.mediainfo.load_cache(f)
+        skin.delete('splashscreen')
+
+    # prepare again, now that all plugins are loaded
+    skin.prepare()
+
+    # start menu
+    MainMenu().getcmd()
+
+    # Kick off the main menu loop
+    _debug_('Main loop starting...',2)
+
+    MainTread()
+    
+    kaa.notifier.loop()
+
+
+except KeyboardInterrupt:
+    print 'Shutdown by keyboard interrupt'
+    # Shutdown the application
+    shutdown()
+
+except SystemExit:
+    pass
+
+except:
+    _debug_('Crash!', config.DCRITICAL)
+    try:
+        tb = sys.exc_info()[2]
+        fname, lineno, funcname, text = traceback.extract_tb(tb)[-1]
+
+        if config.FREEVO_EVENTHANDLER_SANDBOX:
+            secs = 5
+        else:
+            secs = 1
+        for i in range(secs, 0, -1):
+            osd.clearscreen(color=osd.COL_BLACK)
+            osd.drawstring(_('Freevo crashed!'), 70, 70, 
fgcolor=osd.COL_ORANGE)
+            osd.drawstring(_('Filename: %s') % fname, 70, 130, 
fgcolor=osd.COL_ORANGE)
+            osd.drawstring(_('Lineno: %s') % lineno, 70, 160, 
fgcolor=osd.COL_ORANGE)
+            osd.drawstring(_('Function: %s') % funcname, 70, 190, 
fgcolor=osd.COL_ORANGE)
+            osd.drawstring(_('Text: %s') % text, 70, 220, 
fgcolor=osd.COL_ORANGE)
+            osd.drawstring(str(sys.exc_info()[1]), 70, 280, 
fgcolor=osd.COL_ORANGE)
+            osd.drawstring(_('Please see the logfiles for more info'), 70, 350,
+                           fgcolor=osd.COL_ORANGE)
+            osd.drawstring(_('Exit in %s seconds') % i, 70, 410, 
fgcolor=osd.COL_ORANGE)
+            osd.update()
+            time.sleep(1)
+
+    except:
+        pass
+    traceback.print_exc()
+
+    # Shutdown the application, but not the system even if that is
+    # enabled
+    shutdown()

Added: branches/rel-1/testing/Duncan/notifier/rc.py
==============================================================================
--- (empty file)
+++ branches/rel-1/testing/Duncan/notifier/rc.py        Sun Sep 23 15:40:08 2007
@@ -0,0 +1,626 @@
+# -*- coding: iso-8859-1 -*-
+# -----------------------------------------------------------------------
+# rc.py - Remote control / Event and Callback handling
+# -----------------------------------------------------------------------
+# $Id: rc.py 9801 2007-08-01 18:59:46Z duncan $
+#
+# Notes: This is the only class to be thread safe!
+# Todo:
+#
+# -----------------------------------------------------------------------
+# Freevo - A Home Theater PC framework
+# Copyright (C) 2002 Krister Lagerstrom, et al.
+# Please see the file freevo/Docs/CREDITS for a complete list of authors.
+#
+# 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 2 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 MER-
+# CHANTABILITY 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, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# -----------------------------------------------------------------------
+
+
+import os
+import copy
+import time
+import thread
+import types
+
+import kaa.notifier
+
+import config
+import evdev
+
+from event import Event, BUTTON
+
+SHUTDOWN = -1
+
+PYLIRC     = False
+_singleton = None
+
+
+def get_singleton(**kwargs):
+    """
+    get the global rc object
+    """
+    global _singleton
+
+    # One-time init
+    if _singleton == None:
+        _singleton = EventHandler(**kwargs)
+
+    return _singleton
+
+
+def post_event(event):
+    """
+    add an event to the event queue
+    """
+    return get_singleton().post_event(event)
+
+
+def app(application=0):
+    """
+    set or get the current app/eventhandler
+    """
+    if not application == 0:
+        context = 'menu'
+        if hasattr(application, 'app_mode'):
+            context = application.app_mode
+        if hasattr(application, 'eventhandler'):
+            application = application.eventhandler
+        get_singleton().set_app(application, context)
+
+    return get_singleton().get_app()
+
+
+def set_context(context):
+    """
+    set the context (map with button->event transformation
+    """
+    return get_singleton().set_context(context)
+
+
+def register(function, repeat, timer, *arg):
+    """
+    register an function to be called
+    repeat: if true, call the function later again
+    timer:  timer * 0.01 seconds when to call the function
+    """
+    return get_singleton().register(function, repeat, timer, *arg)
+
+
+def unregister(object):
+    """
+    unregister an object from the main loop
+    """
+    return get_singleton().unregister(object)
+
+
+def shutdown():
+    """
+    shutdown the rc
+    """
+    return get_singleton().shutdown()
+
+
+def suspend():
+    """
+    suspend the rc
+    """
+    return get_singleton().suspend()
+
+
+def resume():
+    """
+    resume the rc
+    """
+    return get_singleton().resume()
+
+
+# 
--------------------------------------------------------------------------------
+
+# 
--------------------------------------------------------------------------------
+# internal classes of this module
+# 
--------------------------------------------------------------------------------
+
+class Lirc:
+    """
+    Class to handle lirc events
+    """
+    def __init__(self):
+        try:
+            global pylirc
+            import pylirc
+        except ImportError:
+            print 'WARNING: PyLirc not found, lirc remote control disabled!'
+            raise
+        try:
+            if os.path.isfile(config.LIRCRC):
+                self.resume()
+            else:
+                raise IOError
+        except RuntimeError:
+            print 'WARNING: Could not initialize PyLirc!'
+            raise
+        except IOError:
+            print 'WARNING: %s not found!' % config.LIRCRC
+            raise
+
+        self.nextcode = pylirc.nextcode
+
+        self.previous_code            = None
+        self.repeat_count             = 0
+        self.firstkeystroke           = 0.0
+        self.lastkeystroke            = 0.0
+        self.lastkeycode              = ''
+        self.default_keystroke_delay1 = 0.25  # Config
+        self.default_keystroke_delay2 = 0.25  # Config
+
+        global PYLIRC
+        PYLIRC = True
+
+
+    def resume(self):
+        """
+        (re-)initialize pylirc, e.g. after calling close()
+        """
+        _debug_('PyLirc resumed!')
+        pylirc.init('freevo', config.LIRCRC)
+        pylirc.blocking(0)
+
+
+    def suspend(self):
+        """
+        cleanup pylirc, close devices
+        """
+        _debug_('PyLirc suspended!')
+        pylirc.exit()
+
+
+    def get_last_code(self):
+        """
+        read the lirc interface
+        """
+        result = None
+
+        if self.previous_code != None:
+            # Let's empty the buffer and return the most recent code
+            while 1:
+                list = self.nextcode();
+                if list != []:
+                    break
+        else:
+            list = self.nextcode()
+
+        if list == []:
+            list = None
+
+        if list != None:
+            result = list
+
+        self.previous_code = result
+        return result
+
+
+
+    def poll(self, rc):
+        """
+        return next event
+        """
+        list = self.get_last_code()
+
+        if list == None:
+            nowtime = 0.0
+            nowtime = time.time()
+            if (self.lastkeystroke + self.default_keystroke_delay2 < nowtime) 
and \
+                   (self.firstkeystroke != 0.0):
+                self.firstkeystroke = 0.0
+                self.lastkeystroke = 0.0
+                self.repeat_count = 0
+
+        if list != None:
+            nowtime = time.time()
+
+            if list:
+                for code in list:
+                    if ( self.lastkeycode != code ):
+                        self.lastkeycode = code
+                        self.lastkeystroke = nowtime
+                        self.firstkeystoke = nowtime
+
+            if self.firstkeystroke == 0.0 :
+                self.firstkeystroke = time.time()
+            else:
+                if (self.firstkeystroke + self.default_keystroke_delay1 > 
nowtime):
+                    list = []
+                else:
+                    if (self.lastkeystroke + self.default_keystroke_delay2 < 
nowtime):
+                        self.firstkeystroke = nowtime
+
+            self.lastkeystroke = nowtime
+            self.repeat_count += 1
+
+            for code in list:
+                return code
+
+
+# 
--------------------------------------------------------------------------------
+
+class Keyboard:
+    """
+    Class to handle keyboard input
+    """
+    def __init__(self):
+        """
+        init the keyboard event handler
+        """
+        import osd
+        self.callback = osd.get_singleton()._cb
+
+
+    def poll(self, rc):
+        """
+        return next event
+        """
+        return self.callback(rc.context != 'input')
+
+
+# 
--------------------------------------------------------------------------------
+
+class TcpNetwork:
+    """
+    Class to handle network control via TCP connection instead of UDP.
+    """
+    import socket
+    MAX_MESSAGE_SIZE = 255 # the maximum size of a message
+    def __init__(self):
+        """
+        init the network event handler
+        """
+        self.port = config.REMOTE_CONTROL_TCP_PORT
+        self.host = config.REMOTE_CONTROL_TCP_HOST
+        self.sock = self.socket.socket(self.socket.AF_INET, \
+                self.socket.SOCK_STREAM)
+        self.sock.setsockopt(self.socket.SOL_SOCKET, \
+                self.socket.SO_REUSEADDR, 1)
+        self.sock.setblocking(0)
+        self.sock.bind((self.host, self.port))
+        self.sock.listen(1)
+        self.connections = []
+
+    def poll(self, rc):
+        """
+        return next event
+        """
+        self._getNewConnections()
+
+        throwout = []
+        for conn in self.connections:
+            try:
+                buffer = conn.recv(self.MAX_MESSAGE_SIZE)
+                if len(buffer) == 0:
+                    throwout.append(self.connections.index(conn))
+                else:
+                    return buffer.strip()
+            except self.socket.error, oErr:
+                # if the error is not of typ 11 there is a problem with
+                # the connection, remove it from the list.
+                if oErr[0] != 11:
+                    throwout.append(self.connections.index(conn))
+
+        throwout.reverse()
+        for index in throwout:
+            self.connections.pop(index)
+
+    def _getNewConnections(self):
+        """
+        accept new connections from the socket
+        """
+        try:
+            conn, addr = self.sock.accept()
+            conn.setblocking(0)
+            self.connections.append(conn)
+        except:
+            # do nothing
+            pass
+
+
+
+class Network:
+    """
+    Class to handle network control
+    """
+    def __init__(self):
+        """
+        init the network event handler
+        """
+        import socket
+        self.port = config.REMOTE_CONTROL_PORT
+        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        self.sock.setblocking(0)
+        self.sock.bind(('', self.port))
+
+
+    def poll(self, rc):
+        """
+        return next event
+        """
+        try:
+            return self.sock.recv(100)
+        except:
+            # No data available
+            return None
+
+# 
--------------------------------------------------------------------------------
+
+class Evdev:
+    """
+    Class to handle evdev events
+    """
+    def __init__(self):
+        """
+        init all specified devices
+        """
+        self._devs = []
+
+        for dev in config.EVENT_DEVS:
+            e = None
+
+            if os.path.exists(dev):
+                try:
+                    e = evdev.evdev(dev)
+                except:
+                    print "Problem opening event device '%s'" % dev
+            else:
+                name = dev
+                for dev in os.listdir('/dev/input'):
+                    if not dev.startswith('event'):
+                        continue
+
+                    try:
+                        dev = '/dev/input/' + dev
+                        e = evdev.evdev(dev)
+                    except:
+                        continue
+
+                    if e.get_name() == name:
+                        break
+                else:
+                    e = None
+                    print "Could not find any device named '%s'" % name
+
+            if e is not None:
+                print "Added input device '%s': %s" % (dev, e.get_name())
+                self._devs.append(e)
+
+        self._movements = {}
+
+    def poll(self, rc):
+        """
+        return next event
+        """
+        for dev in self._devs:
+            event = dev.read()
+            if event is None:
+                continue
+
+            time, type, code, value = event
+
+            if type == 'EV_KEY':
+                self._movements = {}
+
+                if config.EVENTMAP.has_key(code):
+                    # 0 = release, 1 = press, 2 = repeat
+                    if value > 0:
+                        return config.EVENTMAP[code]
+            elif type == 'EV_REL':
+                if config.EVENTMAP.has_key(code):
+                    if self._movements.has_key(code):
+                        self._movements[code] += value
+                    else:
+                        self._movements[code] = value
+
+                    if self._movements[code] < -10:
+                        self._movements = {}
+                        return config.EVENTMAP[code][0]
+                    elif self._movements[code] > 10:
+                        self._movements = {}
+                        return config.EVENTMAP[code][1]
+
+# 
--------------------------------------------------------------------------------
+
+class EventHandler:
+    """
+    Class to transform input keys or buttons into events. This class
+    also handles the complete event queue (post_event)
+    """
+    def __init__(self, use_pylirc=1, use_netremote=1):
+
+        self.inputs = []
+        if use_pylirc:
+            try:
+                self.inputs.append(Lirc())
+            except:
+                pass
+
+        if config.USE_SDL_KEYBOARD:
+            try:
+                self.inputs.append(Keyboard())
+            except:
+                pass
+
+        try:
+            self.inputs.append(Evdev())
+        except:
+            pass
+
+        if use_netremote and config.ENABLE_NETWORK_REMOTE and \
+               config.REMOTE_CONTROL_PORT:
+            self.inputs.append(Network())
+
+        if use_netremote and config.ENABLE_TCP_NETWORK_REMOTE and \
+               config.REMOTE_CONTROL_TCP_PORT and \
+               config.REMOTE_CONTROL_TCP_HOST:
+            self.inputs.append(TcpNetwork())
+
+        self.app                = None
+        self.context            = 'menu'
+        self.callbacks          = []
+        self.shutdown_callbacks = []
+        self.poll_objects       = []
+        # lock all critical parts
+        self.lock               = thread.allocate_lock()
+        # last time we stopped sleeping
+        self.sleep_timer        = 0
+        kaa.notifier.Timer(self.poll).start(0.01)
+
+
+    def set_app(self, app, context):
+        """
+        set default eventhandler and context
+        """
+        self.app     = app
+        self.context = context
+
+
+    def get_app(self):
+        """
+        get current eventhandler (app)
+        """
+        return self.app
+
+
+    def set_context(self, context):
+        """
+        set context for key mapping
+        """
+        self.context = context
+
+
+    def post_event(self, e):
+        """
+        add event to the queue
+        """
+        if not isinstance(e, Event):
+            e = Event(e, context=self.context)
+        e.post()
+
+
+    def key_event_mapper(self, key):
+        """
+        map key to event based on current context
+        """
+        if not key:
+            return None
+
+        for c in (self.context, 'global'):
+            try:
+                e = config.EVENTS[c][key]
+                e.context = self.context
+                return e
+            except KeyError:
+                pass
+
+        if self.context != 'input':
+            print 'no event mapping for key %s in context %s' % (key, 
self.context)
+            print 'send button event BUTTON arg=%s' % key
+        return Event(BUTTON, arg=key)
+
+
+    def register(self, function, repeat, timer, *arg):
+        """
+        register an function to be called
+        repeat: if true, call the function later again
+        timer:  timer * 0.01 seconds when to call the function
+        """
+        self.lock.acquire()
+        try:
+            if timer == SHUTDOWN:
+                _debug_('register shutdown callback: %s' % function, 2)
+                self.shutdown_callbacks.append([ function, arg ])
+            else:
+                if repeat:
+                    _debug_('register callback: %s' % function, 2)
+                self.callbacks.append([ function, repeat, timer, 0, arg ])
+        finally:
+            self.lock.release()
+
+
+    def unregister(self, function):
+        """
+        unregister an object from the main loop
+        """
+        self.lock.acquire()
+        try:
+            for c in copy.copy(self.callbacks):
+                if c[0] == function:
+                    _debug_('unregister callback: %s' % function, 2)
+                    self.callbacks.remove(c)
+            for c in copy.copy(self.shutdown_callbacks):
+                if c[0] == function:
+                    _debug_('unregister shutdown callback: %s' % function, 2)
+                    self.shutdown_callbacks.remove(c)
+        finally:
+            self.lock.release()
+
+
+    def suspend(self):
+        for i in self.inputs:
+            if hasattr(i, 'suspend'):
+                i.suspend()
+
+
+    def resume(self):
+        for i in self.inputs:
+            if hasattr(i, 'resume'):
+                i.resume()
+
+
+    def shutdown(self):
+        """
+        shutdown the rc
+        """
+        for c in copy.copy(self.shutdown_callbacks):
+            _debug_('shutting down %s' % c[0], 2)
+            c[0](*c[1])
+
+
+    def poll(self):
+        """
+        poll all registered functions
+        """
+        # search all input objects for new events
+        for i in self.inputs:
+            e = i.poll(self)
+            if e:
+                self.post_event(self.key_event_mapper(e))
+
+        # run all registered callbacks
+        for c in copy.copy(self.callbacks):
+            if c[2] == c[3]:
+                # time is up, call it:
+                if not c[1]:
+                    # remove if it is no repeat callback:
+                    self.lock.acquire()
+                    try:
+                        if c in self.callbacks:
+                            self.callbacks.remove(c)
+                    finally:
+                        self.lock.release()
+                else:
+                    # reset counter for next run
+                    c[3] = 0
+                c[0](*c[4])
+            else:
+                c[3] += 1

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Freevo-cvslog mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/freevo-cvslog

Reply via email to