Customising the Red Not Locked Screen
Customising the Red Not Locked Screen
We use 3 of these for our repeater how easy would it be to prefix the Not Locked with 2m so it reads 2m Not Locked?
Thanks Paul
			
			
									
									
						Thanks Paul
Re: Customising the Red Not Locked Screen
Perfect 
Thank you!
			
			
									
									
						Thank you!
Re: Customising the Red Not Locked Screen
Is it also possible to change de red background color ?
			
			
									
									
						Re: Customising the Red Not Locked Screen
yes,
On your own risk you could play with lots of settings...
Have a look at.
viewtopic.php?f=130&t=7421
Be aware and make backup copies before changing.
I use FTP to get into the raspberry and do all the hard work on my windows pc.
this makes it easyer.. I use 4 ryde for our local Pi6MEP repeater..
All in dutch and yes.. white text on black background...
Best regards
Benno
			
			
									
									
						On your own risk you could play with lots of settings...
Have a look at.
viewtopic.php?f=130&t=7421
Be aware and make backup copies before changing.
I use FTP to get into the raspberry and do all the hard work on my windows pc.
this makes it easyer.. I use 4 ryde for our local Pi6MEP repeater..
All in dutch and yes.. white text on black background...
Best regards
Benno
Re: Customising the Red Not Locked Screen
Here is my custom colour screen:
player.py
			
			
									
									
						player.py
Code: Select all
#    Ryde Player provides a on screen interface and video player for Longmynd compatible tuners.
#    Copyright © 2021 Tim Clark
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <https://www.gnu.org/licenses/>.
import pygame, vlc, select, pydispmanx, yaml, os, pkg_resources, argparse, importlib, functools, sys
import rydeplayer.sources.common
import rydeplayer.sources.longmynd
import rydeplayer.sources.combituner
from . import ir
import rydeplayer.gpio
import rydeplayer.network
import rydeplayer.common
import rydeplayer.states.gui
import rydeplayer.states.playback
import rydeplayer.osd.display
import rydeplayer.osd.modules
# container for the theme
class Theme(object):
    def __init__(self, displaySize):
        self.colours = type('colours', (object,), {
            'transparent': (0,0,0,0),
            'transpBack': (0,0,0,51),
            'black': (0,0,0,255),
            'white': (255,160,0,255),
            'red': (255,0,0,255),
            'textError': (255,0,0,255),
            'backgroundMenu': (192,192,192,255),
            'backgroundSubMenu': (160,160,160,255),
            'backgroundPlayState': (160,160,160,255),
            })
        self.displayWidth = int(displaySize[0])
        self.displayHeight = int(displaySize[1])
        self.menuWidth = int(self.displayWidth/4)
        self.menuHeight = self.displayHeight
        playStateTitleFontSize=self.fontSysSizeOptimize('Not Loaded', displaySize[0]/2, 'freesans')
        menuH1FontSize=self.fontSysSizeOptimize('BATC Ryde Project', self.menuWidth*0.85, 'freesans')
        self.fonts = type('fonts', (object,), {
            'menuH1': pygame.font.SysFont('freesans', menuH1FontSize),
            'playStateTitle' :  pygame.font.SysFont('freesans', playStateTitleFontSize),
            })
        self._circlecache = {}
        self.logofile = pkg_resources.resource_stream('rydeplayer.resources', 'logo_menu.png')
        self.muteicon = pkg_resources.resource_stream('rydeplayer.resources', 'icon_mute.png')
    # calculate the largest font size that you can render the given test in as still be less than width
    def fontSysSizeOptimize(self, text, width, fontname):
        fontsize = -1
        while True:
            fontCandidate = pygame.font.SysFont(fontname, fontsize+1)
            fontwidth = fontCandidate.size(text)[0]
            del(fontCandidate)
            if(fontwidth > width):
                break
            else:
                fontsize += 1
        return fontsize
    # calculate the largest font size that has a line height less than height
    def fontSysSizeOptimizeHeight(self, height, fontname):
        fontsize = -1
        while True:
            fontCandidate = pygame.font.SysFont(fontname, fontsize+1)
            fontheight = fontCandidate.get_linesize()
            del(fontCandidate)
            if(fontheight > height):
                break
            else:
                fontsize += 1
        return fontsize
    # size and position a pygame rectangle using screen size independent units and a datum corner
    def relativeRect(self, datum, xEdgeDistance, yEdgeDistance, width, height):
        outwidth = self.displayHeight*width
        outheight = self.displayHeight*height
        if datum is rydeplayer.common.datumCornerEnum.TR:
            outX = self.displayWidth - ((xEdgeDistance+width)*self.displayHeight)
            outY = yEdgeDistance*self.displayHeight
        elif datum is rydeplayer.common.datumCornerEnum.TC:
            outX = (self.displayWidth/2) - (((width/2)-xEdgeDistance)*self.displayHeight)
            outY = yEdgeDistance*self.displayHeight
        elif datum is rydeplayer.common.datumCornerEnum.TL:
            outX = xEdgeDistance*self.displayHeight
            outY = yEdgeDistance*self.displayHeight
        elif datum is rydeplayer.common.datumCornerEnum.CR:
            outX = self.displayWidth - ((xEdgeDistance+width)*self.displayHeight)
            outY = (self.displayHeight/2) - (((height/2)-yEdgeDistance)*self.displayHeight)
        elif datum is rydeplayer.common.datumCornerEnum.CC:
            outX = (self.displayWidth/2) - (((width/2)-xEdgeDistance)*self.displayHeight)
            outY = (self.displayHeight/2) - (((height/2)-yEdgeDistance)*self.displayHeight)
        elif datum is rydeplayer.common.datumCornerEnum.CL:
            outX = xEdgeDistance*self.displayHeight
            outY = (self.displayHeight/2) - (((height/2)-yEdgeDistance)*self.displayHeight)
        elif datum is rydeplayer.common.datumCornerEnum.BR:
            outX = self.displayWidth - ((xEdgeDistance+width)*self.displayHeight)
            outY = self.displayHeight - ((yEdgeDistance+height)*self.displayHeight)
        elif datum is rydeplayer.common.datumCornerEnum.BC:
            outX = (self.displayWidth/2) - (((width/2)-xEdgeDistance)*self.displayHeight)
            outY = self.displayHeight - ((yEdgeDistance+height)*self.displayHeight)
        elif datum is rydeplayer.common.datumCornerEnum.BL:
            outX = xEdgeDistance*self.displayHeight
            outY = self.displayHeight - ((yEdgeDistance+height)*self.displayHeight)
        return pygame.Rect((outX, outY, outwidth, outheight))
    # helper function for outline font rendering
    def _circlepoints(self,r):
        r = int(round(r))
        if r in self._circlecache:
            return self._circlecache[r]
        x, y, e = r, 0, 1 - r
        self._circlecache[r] = points = []
        while x >= y:
            points.append((x, y))
            y += 1
            if e < 0:
                e += 2 * y - 1
            else:
                x -= 1
                e += 2 * (y - x) - 1
        points += [(y, x) for x, y in points if x > y]
        points += [(-x, y) for x, y in points if x]
        points += [(x, -y) for x, y in points if y]
        points.sort()
        return points
    # render font with different coloured outline
    def outlineFontRender(self, text, font, gfcolor, ocolor, opx=2):
        textsurface = font.render(text, True, gfcolor)
        w = textsurface.get_width() + 2 * opx
        h = font.get_height()
        osurf = pygame.Surface((w, h + 2 * opx), pygame.SRCALPHA)
        osurf.fill((0, 0, 0, 0))
        surf = osurf.copy()
        osurf.blit(font.render(text, True, ocolor), (0, 0))
        for dx, dy in self._circlepoints(opx):
            surf.blit(osurf, (dx + opx, dy + opx))
        surf.blit(textsurface, (opx, opx))
        return surf
# power menu UI state machine
class SubMenuPower(rydeplayer.states.gui.ListSelect):
    def __init__(self, theme, backState, shutdownCallback):
        items = {
                    rydeplayer.common.shutdownBehavior.APPSTOP : 'App Shutdown',
                    rydeplayer.common.shutdownBehavior.APPREST : 'App Restart',
                    rydeplayer.common.shutdownBehavior.SYSSTOP : 'System Shutdown',
                    rydeplayer.common.shutdownBehavior.SYSREST : 'System Restart',
                }
        super().__init__(theme, backState, items, lambda: rydeplayer.common.shutdownBehavior.APPSTOP, shutdownCallback)
    def get_event(self, event):
        if super().get_event(event):
            return True
        else:
            if event == rydeplayer.common.navEvent.POWER:
                return super().get_event(rydeplayer.common.navEvent.SELECT)
            else:
                return False
# main UI state machine
class guiState(rydeplayer.states.gui.SuperStates):
    def __init__(self, theme, shutdownBehaviorDefault, player, osd):
        super().__init__(theme)
        self.done = False
        self.player = player
        self.osd = osd
        self.shutdownBehaviorDefault = shutdownBehaviorDefault
        self.shutdownState = rydeplayer.common.shutdownBehavior.APPSTOP
    # callback to run all the remove and cleanup callback on the active manu states
    def _cleanupMenuStates(self, activeCallbacks):
        for callback in activeCallbacks:
            callback()
    # generate the menu states based on current config capabilites
    def _genMenuStates(self, config, debugFunctions, superMenu):
        # get variables for current config
        tunerConfigVars = config.tuner.getVars()
        # generate config specific menu items
        mainMenuStates = {}
        firstkey = None
        lastkey = None
        # set of cleanup functions for the menu items
        activeCallbacks = set()
        for key in tunerConfigVars:
            validVar = False
            # create sub menu items for supported var types
            if isinstance(tunerConfigVars[key], rydeplayer.sources.common.tunerConfigIntList):
                mainMenuStates[key+'-sel'] = rydeplayer.states.gui.MultipleNumberSelect(self.theme, key, tunerConfigVars[key], config.tuner.runCallbacks)
                validVar = True
            if isinstance(tunerConfigVars[key], rydeplayer.sources.common.tunerConfigInt):
                mainMenuStates[key+'-sel'] = rydeplayer.states.gui.NumberSelect(self.theme, key, tunerConfigVars[key], config.tuner.runCallbacks)
                validVar = True
            if validVar:
                if firstkey is None:
                    firstkey = key
                if lastkey is None:
                    lastkey = key
                # create menu item
                mainMenuStates[key] = rydeplayer.states.gui.MenuItem(self.theme, tunerConfigVars[key].getLongName(), lastkey, firstkey, key+'-sel', tunerConfigVars[key])
                mainMenuStates[lastkey].down = key
                mainMenuStates[firstkey].up = key
                lastkey = key
                # callback to menu header for validity updates
                validCallFunc = functools.partial(superMenu.redrawState, mainMenuStates[key], mainMenuStates[key].getSurfaceRects())
                tunerConfigVars[key].addValidCallback(validCallFunc)
                activeCallbacks.add(functools.partial(tunerConfigVars[key].removeValidCallback,validCallFunc))
        cleanupFunc = functools.partial(self._cleanupMenuStates, activeCallbacks)
        # main menu states, order is important to get menus and sub menus to display in the right place
        if firstkey is None:
            firstkey = 'band'
        if lastkey is None:
            lastkey = 'power'
        baseMenuStates = {
            'band-sel'  : rydeplayer.states.gui.ListSelect(self.theme, 'band', config.bands, config.tuner.getBand, config.tuner.setBand),
            'band'      : rydeplayer.states.gui.MenuItem(self.theme, "Band", lastkey, "preset", "band-sel"),
            'preset-sel'  : rydeplayer.states.gui.ListSelect(self.theme, 'preset', config.presets, lambda:config.tuner, config.tuner.setConfigToMatch),
            'preset'      : rydeplayer.states.gui.MenuItem(self.theme, "Presets", "band", "power", "preset-sel"),
            'power-sel'  : SubMenuPower(self.theme, 'power', self.shutdown),
            'power'      : rydeplayer.states.gui.MenuItem(self.theme, "Power", "preset", firstkey, "power-sel"),
        }
        mainMenuStates.update(baseMenuStates)
        mainMenuStates[lastkey].down = 'band'
        mainMenuStates[firstkey].up = 'power'
        lastkey = 'power'
        
        # add debug menu if enabled in config
        if config.debug.enableMenu:
            # generate debug menu states
            debugMenuStates = {}
            debugPrevState = None
            debugFirstState = None
            for key in debugFunctions:
                menukey = key.strip().replace(" ", "").lower()
                if debugFirstState is None:
                    debugFirstState = menukey
                    debugPrevState = menukey
                debugMenuStates[menukey] = rydeplayer.states.gui.SubMenuItemFunction(self.theme, key, debugPrevState, debugFirstState, debugFunctions[key])
                debugMenuStates[debugPrevState].down = menukey
                debugMenuStates[debugFirstState].up = menukey
                debugPrevState = menukey
            mainMenuStates['debug-sel'] = rydeplayer.states.gui.SubMenuGeneric(self.theme, 'debug', debugMenuStates, debugFirstState)
            mainMenuStates['debug'] = rydeplayer.states.gui.MenuItem(self.theme, "Debug", lastkey, firstkey, "debug-sel")
            mainMenuStates[lastkey].down = 'debug'
            mainMenuStates[firstkey].up = 'debug'
            lastkey = 'debug'
        validCallFunc = superMenu.refreshStates
        config.tuner.addVarChangeCallbackFunction(validCallFunc)
        activeCallbacks.add(functools.partial(config.tuner.removeVarChangeCallbackFunction,validCallFunc))
        return (mainMenuStates, firstkey, cleanupFunc)
    def startup(self, config, debugFunctions):
        # top level state machine
        self.state_dict = {
            'menu': rydeplayer.states.gui.Menu(self.theme, 'home', functools.partial(self._genMenuStates, config, debugFunctions)),
            'home': Home(self.theme, self.osd)
        }
        self.state_name = "home"
        self.state = self.state_dict[self.state_name]
        self.state.startup()
    def shutdown(self, shutdownState):
        self.shutdownState = shutdownState
        self.state.cleanup()
        self.done = True
    def get_event(self, event):
        if not self.state.get_event(event):
            if event == rydeplayer.common.navEvent.POWER:
                self.setStateStack([self.shutdownBehaviorDefault,'power-sel','menu'])
            elif event == rydeplayer.common.navEvent.OSDON:
                self.osd.activate(1)
            elif event == rydeplayer.common.navEvent.OSDOFF:
                self.osd.deactivate(1)
            elif event == rydeplayer.common.navEvent.OSDTOG:
                self.osd.toggle(2)
            elif(event == rydeplayer.common.navEvent.MUTE):
                self.player.toggleMute()
            elif(event == rydeplayer.common.navEvent.VOLU):
                self.player.adjustVolumeByStep(True)
                self.player.setMute(False)
                self.osd.activate(3, rydeplayer.osd.display.TimerLength.USERTRIGGER)
            elif(event == rydeplayer.common.navEvent.VOLD):
                self.player.adjustVolumeByStep(False)
                self.player.setMute(False)
                self.osd.activate(3, rydeplayer.osd.display.TimerLength.USERTRIGGER)
            elif(event == rydeplayer.common.navEvent.CHANU):
                self.player.switchPresetRelative(-1)
            elif(event == rydeplayer.common.navEvent.CHAND):
                self.player.switchPresetRelative(1)
# GUI state for when the menu isnt showing
class Home(rydeplayer.states.gui.States):
    def __init__(self, theme, osd):
        super().__init__(theme)
        self.next = 'menu'
        self.osd = osd
    def cleanup(self):
        None
    def startup(self):
        None
    def get_event(self, event):
        #TODO: add OSD state machine
        if(event == rydeplayer.common.navEvent.SELECT):
            self.osd.activate(3, rydeplayer.osd.display.TimerLength.USERTRIGGER)
        elif(event == rydeplayer.common.navEvent.BACK):
            self.osd.deactivate(2)
        elif(event == rydeplayer.common.navEvent.MENU):
            self.done = True
class rydeConfig(object):
    def __init__(self, theme):
        self.ir = ir.irConfig()
        self.gpio = rydeplayer.gpio.gpioConfig()
        self.tuner = rydeplayer.sources.common.tunerConfig()
        # source specific config
        self.sourceConfigs = dict()
        for thisSource in rydeplayer.sources.common.sources:
            self.sourceConfigs[thisSource]=thisSource.getSource().getConfig()()
        self.bands = {}
        defaultBand = self.tuner.getBand()
        self.bands[defaultBand] = "None"
        self.presets = {}
        self.osd = rydeplayer.osd.display.Config(theme)
        self.network = rydeplayer.network.networkConfig()
        self.shutdownBehavior = rydeplayer.common.shutdownBehavior.APPSTOP
        self.audio = type('audioConfig', (object,), {
            'muteOnStartup': False,
            'volumeOnStartup': 100,
            'volumeStep': 25,
            })
        self.debug = type('debugConfig', (object,), {
            'enableMenu': False,
            'autoplay': True,
            'disableHardwareCodec': True,
            })
        self.configRev = 3
    #setter for default values
    def setAutoplay(self, newval):
        self.debug.autoplay = newval
    # parse config dict
    def loadConfig(self, config):
        perfectConfig = True
        if isinstance(config, dict):
            # parse config revision
            if 'configRev' in config:
                if isinstance(config['configRev'], int):
                    if config['configRev'] != self.configRev:
                        print("Unmatched config revision, config load aborted")
                        return False
                else:
                    print("Config revision not an integer, config load aborted")
                    return False
            else:
                print("WARNING: no config revision present, config load my fail")
            if 'bands' in config:
                if isinstance(config['bands'], dict):
                    newBands = {}
                    exsistingBands = list(self.bands.keys())
                    for bandName in config['bands']:
                        bandDict = config['bands'][bandName]
                        bandParseSuccess, bandObject = rydeplayer.sources.common.tunerBand.loadBand(bandDict)
                        if bandParseSuccess:
                            # dedupe band object with exsisting library
                            if bandObject in exsistingBands:
                                bandObject = exsistingBands[exsistingBands.index(bandObject)]
                            newBands[bandObject] = str(bandName)
                        else:
                            perfectConfig = False
                    if len(newBands) > 1:
                        self.bands = newBands
                    else:
                        print("No valid bands, skipping")
                        perfectConfig = False
                else:
                    print("Invalid band library")
                    perfectConfig = False
            # parse source specific configs
            if 'sources' in config:
                if isinstance(config['sources'], dict):
                    for thisSource in rydeplayer.sources.common.sources:
                        if thisSource.name.lower() in config['sources']:
                            perfectConfig = perfectConfig and self.sourceConfigs[thisSource].loadConfig(config['sources'][thisSource.name.lower()])
                        elif thisSource.name.upper() in config['sources']:
                            perfectConfig = perfectConfig and self.sourceConfigs[thisSource].loadConfig(config['sources'][thisSource.name.upper()])
                else:
                    print("Sources config not a dict")
                    perfectConfig = False
            # parse presets
            if 'presets' in config:
                if isinstance(config['presets'], dict):
                    newPresets = {}
                    exsistingPresets = list(self.presets.keys())
                    for presetName in config['presets']:
                        presetDict = config['presets'][presetName]
                        presetObject = rydeplayer.sources.common.tunerConfig()
                        if presetObject.loadConfig(presetDict, list(self.bands.keys())):
                            # dedupe preset object with exsisting library
                            if presetObject in exsistingPresets:
                                presetObject = exsistingPresets[exsistingPresets.index(presetObject)]
                            newPresets[presetObject] = str(presetName)
                        else:
                            perfectConfig = False
                    if len(newPresets) > 1:
                        self.presets = newPresets
                    else:
                        print("No valid presets, skipping")
                        perfectConfig = False
                else:
                    print("Invalid preset library")
                    perfectConfig = False
            # pass default tuner config to be parsed by longmynd module
            if 'default' in config:
                defaultPreset = rydeplayer.sources.common.tunerConfig()
#                perfectConfig = perfectConfig and defaultPreset.loadConfig(config['default'], list(self.bands.keys()))
                if defaultPreset.loadConfig(config['default'], list(self.bands.keys())):
                    # dedupe preset object with exsisting library
                    exsistingPresets = list(self.presets.keys())
                    if defaultPreset in exsistingPresets:
                        defaultPreset = exsistingPresets[exsistingPresets.index(defaultPreset)]
                    self.tuner.setConfigToMatch(defaultPreset)
                else:
                    perfectConfig = False
            # pass ir config to be parsed by the ir config container
            if 'ir' in config:
                perfectConfig = perfectConfig and self.ir.loadConfig(config['ir'])
            # pass the gpio config to be parsed by the gpio config container
            if 'gpio' in config:
                perfectConfig = perfectConfig and self.gpio.loadConfig(config['gpio'])
            # pass the osd config to be parsed by the osd config container
            if 'osd' in config:
                perfectConfig = perfectConfig and self.osd.loadConfig(config['osd'])
            # pass the network config to be parsed by the network config container
            if 'network' in config:
                perfectConfig = perfectConfig and self.network.loadConfig(config['network'])
            # parse shutdown default shutdown event behavior
            if 'shutdownBehavior' in config:
                if isinstance(config['shutdownBehavior'], str):
                    validShutBehav = False
                    for shutBehavOpt in rydeplayer.common.shutdownBehavior:
                        if shutBehavOpt.name == config['shutdownBehavior'].upper():
                            self.shutdownBehavior = shutBehavOpt
                            validShutBehav = True
                            break
                    if not validShutBehav:
                        print("Shutdown behavior default invalid, skipping")
                        perfectConfig = False
                else:
                    print("Shutdown behavior default invalid, skipping")
                    perfectConfig = False
            # parse audio options
            if 'audio' in config:
                if isinstance(config['audio'], dict):
                    if 'muteOnStartup' in config['audio']:
                        if isinstance(config['audio']['muteOnStartup'], bool):
                            self.audio.muteOnStartup = config['audio']['muteOnStartup']
                        else:
                            print("Invalid mute on startup config, skipping")
                            perfectConfig = False
                    if 'volumeOnStartup' in config['audio']:
                        if isinstance(config['audio']['volumeOnStartup'], int):
                            if config['audio']['volumeOnStartup'] <= 100 and config['audio']['volumeOnStartup'] >= 0:
                                self.audio.volumeOnStartup = config['audio']['volumeOnStartup']
                            else:
                                print("Invalid startup volume config, out of range, skipping")
                                perfectConfig = False
                        else:
                            print("Invalid startup volume config, skipping")
                            perfectConfig = False
                    if 'volumeStep' in config['audio']:
                        if isinstance(config['audio']['volumeStep'], int):
                            if config['audio']['volumeStep'] <= 100 and config['audio']['volumeStep'] >= 0:
                                self.audio.volumeStep = config['audio']['volumeStep']
                            else:
                                print("Invalid volume step config, out of range, skipping")
                                perfectConfig = False
                        else:
                            print("Invalid volume step config, skipping")
                            perfectConfig = False
            # parse debug options
            if 'debug' in config:
                if isinstance(config['debug'], dict):
                    if 'enableMenu' in config['debug']:
                        if isinstance(config['debug']['enableMenu'], bool):
                            self.debug.enableMenu = config['debug']['enableMenu']
                        else:
                            print("Invalid debug menu config, skipping")
                            perfectConfig = False
                    if 'autoplay' in config['debug']:
                        if isinstance(config['debug']['autoplay'], bool):
                            self.debug.autoplay = config['debug']['autoplay']
                        else:
                            print("Invalid debug autoplay config, skipping")
                            perfectConfig = False
                    if 'disableHardwareCodec' in config['debug']:
                        if isinstance(config['debug']['disableHardwareCodec'], bool):
                            self.debug.disableHardwareCodec = config['debug']['disableHardwareCodec']
                        else:
                            print("Invalid debug hardware codec config, skipping")
                            perfectConfig = False
                else:
                    print("Invalid debug config, skipping")
                    perfectConfig = False
                    
        else:
            print("Invalid config, no fields")
            perfectConfig = False
        return perfectConfig
    # load yaml config file
    def loadFile(self, path):
        if os.path.exists(path) and os.path.isfile(path):
            try:
                with open(path, 'r') as ymlconfigfile:
                    self.loadConfig(yaml.load(ymlconfigfile))
            except IOError as e:
                print(e)
        else:
            print("config file not found")
class player(object):
    def __init__(self, configFile = None):
        # Autodetect output display
        if(len(pydispmanx.getDisplays())<1):
            raise RuntimeError('No displays detected')
        else:
            self.displayId = pydispmanx.getDisplays()[0]
        # setup ui core
        pygame.init()
        self.theme = Theme(pydispmanx.getDisplaySize())
        self.playbackState = rydeplayer.states.playback.StateDisplay(self.theme)
        print(pygame.font.get_fonts())
        # load config
        self.config = rydeConfig(self.theme)
        if configFile != None:
            self.config.loadFile(configFile)
        print(self.config.tuner)
        # mute
        self.mute = self.config.audio.muteOnStartup
        self.muteCallbacks = []
        # volume
        self.volume = self.config.audio.volumeOnStartup
        self.volumeCallbacks = []
        # setup source 
        self.sourceMan = rydeplayer.sources.common.sourceManagerThread(self.config.tuner, self.config.sourceConfigs)
        self.config.tuner.addCallbackFunction(self.sourceMan.reconfig)
        self.vlcStartup()
        # setup on screen display
        self.osd = rydeplayer.osd.display.Controller(self.theme, self.config.osd, self.sourceMan.getStatus(), self, self.config.tuner)
        debugFunctions = {'Restart Source':self.sourceMan.restart, 'Force VLC':self.vlcStop, 'Abort VLC': self.vlcAbort}
        # start ui
        self.app = guiState(self.theme, self.config.shutdownBehavior, self, self.osd)
        self.app.startup(self.config, debugFunctions)
        # start network
        self.netMan = rydeplayer.network.networkManager(self.config, self.stepSM, self.setMute, debugFunctions)
        # setup ir
        self.irMan = ir.irManager(self.stepSM, self.config.ir)
        # setup gpio
        self.gpioMan = rydeplayer.gpio.gpioManager(self.stepSM, self.config.gpio, self.config.tuner)
        self.config.tuner.addCallbackFunction(self.gpioMan.setBandOutFromPreset)
        # start source
        self.sourceMan.start()
        print("Ready")
        self.monotonicState = 0;
    def start(self):
        quit = False
        # main event loop
        while not quit:
            # need to regen every loop, lm stdout handler changes on lm restart
            fds = self.irMan.getFDs() + self.sourceMan.getFDs() + self.gpioMan.getFDs() + self.osd.getFDs() + self.netMan.getFDs()
            r, w, x = select.select(fds, [], [])
            for fd in r:
                quit = self.handleEvent(fd)
                self.updateState()
                if quit:
                    break
        self.shutdown(self.app.shutdownState)
    def addMuteCallback(self, callback):
        self.muteCallbacks.append(callback)
    def removeMuteCallback(self, callback):
        self.muteCallbacks.remove(callback)
    def setMute(self,newMute):
        if self.mute != newMute:
            self.mute = newMute
            self.vlcPlayer.audio_set_mute(newMute)
            for callback in self.muteCallbacks:
                callback(newMute)
    def getMute(self):
        return self.mute
    def toggleMute(self):
        self.setMute(not self.mute)
    def addVolumeCallback(self, callback):
        self.volumeCallbacks.append(callback)
    def removeVolumeCallback(self, callback):
        self.volumeCallbacks.remove(callback)
    def setVolume(self,newVolume):
        if newVolume > 100:
            newVolume = 100
        elif newVolume < 0:
            newVolume = 0
        if self.volume != newVolume:
            self.volume = newVolume
            self.vlcPlayer.audio_set_volume(newVolume)
            for callback in self.volumeCallbacks:
                callback(newVolume)
    def getVolume(self):
        return self.volume
    def adjustVolume(self, volumeAdjustment):
        self.setVolume(self.volume + volumeAdjustment)
    def adjustVolumeByStep(self, up):
        if up:
            self.adjustVolume(self.config.audio.volumeStep)
        else:
            self.adjustVolume(-self.config.audio.volumeStep)
    def getPresetName(self, preset):
        if preset in self.config.presets:
            return str(self.config.presets[preset])
        else:
            return ""
    def switchPresetRelative(self, offset):
        presetkeys = list(self.config.presets.keys())
        if len(presetkeys) > 0:
            newindex = None
            if self.config.tuner in presetkeys:
                newindex = (presetkeys.index(self.config.tuner) + offset)%len(presetkeys)
            else:
                if offset > 0:
                    newindex = 0
                elif offset < 0:
                    newindex = len(presetkeys)-1
            if newindex is not None:
                self.config.tuner.setConfigToMatch(presetkeys[newindex])
                self.osd.activate(3, rydeplayer.osd.display.TimerLength.USERTRIGGER)
    def shutdown(self, behaviour):
        del(self.osd)
        del(self.playbackState)
        self.sourceMan.shutdown()
        if behaviour is rydeplayer.common.shutdownBehavior.APPREST:
            os.execv(sys.executable, ['python3', '-m', 'rydeplayer'] + sys.argv[1:])
        elif behaviour is rydeplayer.common.shutdownBehavior.SYSSTOP:
            os.system("sudo shutdown -h now")
        elif behaviour is rydeplayer.common.shutdownBehavior.SYSREST:
            os.system("sudo shutdown -r now")
    def handleEvent(self, fd):
        quit = False
        # handle ready file descriptors
        if(fd in self.irMan.getFDs()):
            quit = self.irMan.handleFD(fd)
        elif(fd in self.sourceMan.getFDs()):
            self.sourceMan.handleFD(fd)
        elif(fd in self.gpioMan.getFDs()):
            quit = self.gpioMan.handleFD(fd)
        elif(fd in self.osd.getFDs()):
            quit = self.osd.handleFD(fd)
        elif(fd in self.netMan.getFDs()):
            quit = self.netMan.handleFD(fd)
        return quit
    def updateState(self):
        # update playback state
        state = self.sourceMan.getCoreState()
        if(state.isRunning):
            if(state.isLocked):
                self.playbackState.setState(rydeplayer.states.playback.States.LOCKED)
                newMonoState = state.monotonicState
                if self.monotonicState != newMonoState:
                    self.monotonicState = newMonoState
                    self.vlcStop()
                    print("Param Restart")
                if self.vlcPlayer.get_state() not in [vlc.State.Playing, vlc.State.Opening] and self.config.debug.autoplay:
                    self.vlcPlay()
                    self.osd.activate(4, rydeplayer.osd.display.TimerLength.PROGRAMTRIGGER)
                self.gpioMan.setRXgood(True)
            else:
                self.playbackState.setState(rydeplayer.states.playback.States.NOLOCK)
                if self.config.debug.autoplay:
                    self.vlcStop()
                self.gpioMan.setRXgood(False)
        else:
            if state.isStarted:
                self.playbackState.setState(rydeplayer.states.playback.States.SOURCELOAD)
            else:
                self.playbackState.setState(rydeplayer.states.playback.States.NOSOURCE)
            if self.config.debug.autoplay:
                self.vlcStop()
#               print("parsed:"+str(vlcMedia.is_parsed()))
            self.gpioMan.setRXgood(False)
        self.vlcPlayer.audio_set_mute(self.mute)
        self.vlcPlayer.audio_set_volume(self.volume)
        print(self.vlcPlayer.get_state())
    # Step the state machine with a navEvent
    def stepSM(self, code):
        self.app.get_event(code)
        if(self.app.done):
            self.app.cleanup()
            return True
        self.app.update()
        return False
    # retrigger vlc to play, mostly exsists as its needed as a callback
    def vlcPlay(self):
        self.vlcPlayer.set_media(self.vlcMedia)
        self.vlcPlayer.play()
    def vlcStop(self):
        self.vlcPlayer.stop()
    def vlcAbort(self):
        del(self.vlcMedia)
        del(self.vlcPlayer)
        del(self.vlcInstance)
        importlib.reload(vlc)
        self.sourceMan.remedia()
#        self.sourceMan.restart()
        self.vlcStartup()
    def vlcStartup(self):
        vlcArgs = ''
        displaySize = pydispmanx.getDisplaySize()
        vlcArgs += '--width '+str(displaySize[0])+' --height '+str(displaySize[1])+' '
        displayPAR = pydispmanx.getPixelAspectRatio()
        vlcArgs += '--monitor-par '+str(displayPAR[0])+':'+str(displayPAR[1])+' '
        # vlc needs manually telling if using the second HDMI
        if self.displayId == 7:
            vlcArgs += '--mmal-display hdmi-2 '
        if self.config.debug.disableHardwareCodec:
            vlcArgs += '--codec ffmpeg '
#        vlcArgs += '--gain 4 --alsa-audio-device hw:CARD=Headphones,DEV=0 '
        print(vlcArgs)
        self.vlcInstance = vlc.Instance(vlcArgs)
        self.vlcPlayer = self.vlcInstance.media_player_new()
        self.vlcMedia = self.vlcInstance.media_new_fd(self.sourceMan.getMediaFd().fileno())
def run():
    parser = argparse.ArgumentParser()
    parser.add_argument(metavar="config filename", dest='conffile', help="YAML config file to try and load. Default: config.yaml", nargs='?', default='config.yaml')
    args = parser.parse_args()
    print(args)
    newplayer = player(args.conffile)
    newplayer.start()
if __name__ == '__main__':
    run()