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