add missing save for videomode config entries
[enigma2.git] / lib / python / Plugins / SystemPlugins / Videomode / VideoHardware.py
1 from Screens.Screen import Screen
2 from Plugins.Plugin import PluginDescriptor
3
4 from enigma import eTimer
5
6 from Components.ActionMap import ActionMap
7 from Components.Label import Label
8 from Components.Pixmap import Pixmap
9 from Screens.MessageBox import MessageBox
10 from Screens.Setup import SetupSummary
11 from Components.ConfigList import ConfigListScreen
12 from Components.config import getConfigListEntry, config, ConfigSelection, ConfigSubDict, ConfigYesNo
13
14 from Tools.CList import CList
15
16 # The "VideoHardware" is the interface to /proc/stb/video.
17 # It generates hotplug events, and gives you the list of 
18 # available and preferred modes, as well as handling the currently
19 # selected mode. No other strict checking is done.
20 class VideoHardware:
21         rates = { } # high-level, use selectable modes.
22
23         modes = { }  # a list of (high-level) modes for a certain port.
24
25         rates["PAL"] =                  { "50Hz":               { 50: "pal", 60: "pal"},
26                                                                                                 "60Hz":         { 50: "pal60", 60: "pal60"},
27                                                                                                 "multi":        { 50: "pal", 60: "pal60"} }
28         rates["NTSC"] =                 { "60Hz":       { 50: "ntsc", 60: "ntsc"} }
29         rates["Multi"] =                { "multi":      { 50: "pal", 60: "ntsc"} }
30         rates["720p"] =                 {       "50Hz":         { 50: "720p50", 60: "720p50"},
31                                                                                                 "60Hz":         { 50: "720p", 60: "720p"},
32                                                                                                 "multi":        { 50: "720p50", 60: "720p"} }
33         rates["1080i"] =                { "50Hz":               { 50: "1080i50", 60: "1080i50"},
34                                                                                                 "60Hz":         { 50: "1080i", 60: "1080i"},
35                                                                                                 "multi":        { 50: "1080i50", 60: "1080i"} }
36         rates["PC"] = { 
37                 "1024x768": { 60: "1024x768"}, # not possible on DM7025
38                 "800x600" : { 60: "800x600"},  # also not possible
39                 "720x480" : { 60: "720x480"},
40                 "720x576" : { 60: "720x576"},
41                 "1280x720": { 60: "1280x720"},
42                 "1280x720 multi": { 50: "1280x720_50", 60: "1280x720"},
43                 "1920x1080": { 60: "1920x1080"},
44                 "1920x1080 multi": { 50: "1920x1080", 60: "1920x1080_50"},
45                 "1280x1024" : { 60: "1280x1024"},
46                 "1366x768" : { 60: "1366x768"},
47                 "1366x768 multi" : { 50: "1366x768", 60: "1366x768_50"},
48                 "1280x768": { 60: "1280x768"},
49                 "640x480" : { 60: "640x480"} 
50         }
51
52         modes["Scart"] = ["PAL", "NTSC", "Multi"]
53         modes["YPbPr"] = ["720p", "1080i"]
54         modes["DVI"] = ["720p", "1080i", "PC"]
55
56         widescreen_modes = set(["720p", "1080i"])
57
58         def __init__(self):
59                 self.last_modes_preferred =  [ ]
60                 self.on_hotplug = CList()
61                 self.standby = False
62                 self.current_mode = None
63                 self.current_port = None
64
65                 self.readAvailableModes()
66
67                 self.createConfig()
68 #               self.on_hotplug.append(self.createConfig)
69
70                 self.readPreferredModes()
71
72                 # take over old AVSwitch component :)
73                 from Components.AVSwitch import AVSwitch
74 #               config.av.colorformat.notifiers = [ ] 
75                 config.av.aspectratio.notifiers = [ ]
76                 config.av.tvsystem.notifiers = [ ]
77                 config.av.wss.notifiers = [ ]
78                 AVSwitch.setInput = self.AVSwitchSetInput
79
80                 config.av.aspect.addNotifier(self.updateAspect)
81                 config.av.wss.addNotifier(self.updateAspect)
82                 config.av.policy_169.addNotifier(self.updateAspect)
83                 config.av.policy_43.addNotifier(self.updateAspect)
84
85                 # until we have the hotplug poll socket
86 #               self.timer = eTimer()
87 #               self.timer.callback.append(self.readPreferredModes)
88 #               self.timer.start(1000)
89
90                 config.av.colorformat.addNotifier(self.updateFastblank) 
91
92         def AVSwitchSetInput(self, mode):
93                 self.standby = mode == "SCART"
94                 self.updateStandby()
95
96         def readAvailableModes(self):
97                 try:
98                         modes = open("/proc/stb/video/videomode_choices").read()[:-1]
99                 except IOError:
100                         print "couldn't read available videomodes."
101                         self.modes_available = [ ]
102                         return
103                 self.modes_available = modes.split(' ')
104
105         def readPreferredModes(self):
106                 try:
107                         modes = open("/proc/stb/video/videomode_preferred").read()[:-1]
108                         self.modes_preferred = modes.split(' ')
109                 except IOError:
110                         print "reading preferred modes failed, using all modes"
111                         self.modes_preferred = self.modes_available
112
113                 if self.modes_preferred != self.last_modes_preferred:
114                         self.last_modes_preferred = self.modes_preferred
115                         print "hotplug on dvi"
116                         self.on_hotplug("DVI") # must be DVI
117
118         # check if a high-level mode with a given rate is available.
119         def isModeAvailable(self, port, mode, rate):
120                 rate = self.rates[mode][rate]
121                 for mode in rate.values():
122                         # DVI modes must be in "modes_preferred"
123 #                       if port == "DVI":
124 #                               if mode not in self.modes_preferred and not config.av.edid_override.value:
125 #                                       print "no, not preferred"
126 #                                       return False
127                         if mode not in self.modes_available:
128                                 return False
129                 return True
130
131         def isWidescreenMode(self, port, mode):
132                 return mode in self.widescreen_modes
133
134         def setMode(self, port, mode, rate, force = None):
135                 print "setMode - port:", port, "mode:", mode, "rate:", rate
136                 # we can ignore "port"
137                 self.current_mode = mode
138                 self.current_port = port
139                 modes = self.rates[mode][rate]
140
141                 mode_50 = modes.get(50)
142                 mode_60 = modes.get(60)
143                 if mode_50 is None or force == 60:
144                         mode_50 = mode_60
145                 if mode_60 is None or force == 50: 
146                         mode_60 = mode_50
147
148                 try:
149                         open("/proc/stb/video/videomode_50hz", "w").write(mode_50)
150                         open("/proc/stb/video/videomode_60hz", "w").write(mode_60)
151                 except IOError:
152                         try:
153                                 # fallback if no possibility to setup 50/60 hz mode
154                                 open("/proc/stb/video/videomode", "w").write(mode_50)
155                         except IOError:
156                                 print "setting videomode failed."
157
158                 try:
159                         open("/etc/videomode", "w").write(mode_50) # use 50Hz mode (if available) for booting
160                 except IOError:
161                         print "writing initial videomode to /etc/videomode failed."
162
163                 self.updateAspect(None)
164
165         def saveMode(self, port, mode, rate):
166                 config.av.videoport.value = port
167                 config.av.videoport.save()
168                 config.av.videomode[port].value = mode
169                 config.av.videomode[port].save()
170                 config.av.videorate[mode].value = rate
171                 config.av.videorate[mode].save()
172
173         def isPortAvailable(self, port):
174                 # fixme
175                 return True
176
177         def isPortUsed(self, port):
178                 if port == "DVI":
179                         self.readPreferredModes()
180                         return len(self.modes_preferred) != 0
181                 else:
182                         return True
183
184         def getPortList(self):
185                 return [port for port in self.modes if self.isPortAvailable(port)]
186
187         # get a list with all modes, with all rates, for a given port.
188         def getModeList(self, port):
189                 print "getModeList for port", port
190                 res = [ ]
191                 for mode in self.modes[port]:
192                         # list all rates which are completely valid
193                         rates = [rate for rate in self.rates[mode] if self.isModeAvailable(port, mode, rate)]
194
195                         # if at least one rate is ok, add this mode
196                         if len(rates):
197                                 res.append( (mode, rates) )
198                 return res
199
200         def createConfig(self, *args):
201                 # create list of output ports
202                 portlist = self.getPortList()
203
204                 # create list of available modes
205                 config.av.videoport = ConfigSelection(choices = [(port, _(port)) for port in portlist])
206                 config.av.videomode = ConfigSubDict()
207                 config.av.videorate = ConfigSubDict()
208
209                 for port in portlist:
210                         modes = self.getModeList(port)
211                         if len(modes):
212                                 config.av.videomode[port] = ConfigSelection(choices = [mode for (mode, rates) in modes])
213                         for (mode, rates) in modes:
214                                 config.av.videorate[mode] = ConfigSelection(choices = rates)
215
216         def setConfiguredMode(self):
217                 port = config.av.videoport.value
218                 if port not in config.av.videomode:
219                         print "current port not available, not setting videomode"
220                         return
221
222                 mode = config.av.videomode[port].value
223
224                 if mode not in config.av.videorate:
225                         print "current mode not available, not setting videomode"
226                         return
227
228                 rate = config.av.videorate[mode].value
229                 self.setMode(port, mode, rate)
230
231         def updateAspect(self, cfgelement):
232                 # determine aspect = {any,4:3,16:9,16:10}
233                 # determine policy = {bestfit,letterbox,panscan,nonlinear}
234
235                 # based on;
236                 #   config.av.videoport.value: current video output device
237                 #     Scart: 
238                 #   config.av.aspect:
239                 #     4_3:            use policy_169
240                 #     16_9,16_10:     use policy_43
241                 #     auto            always "bestfit"
242                 #   config.av.policy_169
243                 #     letterbox       use letterbox
244                 #     panscan         use panscan
245                 #     scale           use bestfit
246                 #   config.av.policy_43
247                 #     pillarbox       use panscan
248                 #     panscan         use letterbox  ("panscan" is just a bad term, it's inverse-panscan)
249                 #     nonlinear       use nonlinear
250                 #     scale           use bestfit
251
252                 port = config.av.videoport.value
253                 if port not in config.av.videomode:
254                         print "current port not available, not setting videomode"
255                         return
256                 mode = config.av.videomode[port].value
257
258                 force_widescreen = self.isWidescreenMode(port, mode)
259
260                 is_widescreen = force_widescreen or config.av.aspect.value in ["16_9", "16_10"]
261                 is_auto = config.av.aspect.value == "auto"
262
263                 if is_widescreen:
264                         if force_widescreen:
265                                 aspect = "16:9"
266                         else:
267                                 aspect = {"16_9": "16:9", "16_10": "16:10"}[config.av.aspect.value]
268                         policy = {"pillarbox": "panscan", "panscan": "letterbox", "nonlinear": "nonlinear", "scale": "bestfit"}[config.av.policy_43.value]
269                 elif is_auto:
270                         aspect = "any"
271                         policy = "bestfit"
272                 else:
273                         aspect = "4:3"
274                         policy = {"letterbox": "letterbox", "panscan": "panscan", "scale": "bestfit"}[config.av.policy_169.value]
275
276                 if not config.av.wss.value:
277                         wss = "auto(4:3_off)"
278                 else:
279                         wss = "auto"
280
281                 print "-> setting aspect, policy, wss", aspect, policy, wss
282                 open("/proc/stb/video/aspect", "w").write(aspect)
283                 open("/proc/stb/video/policy", "w").write(policy)
284                 open("/proc/stb/denc/0/wss", "w").write(wss)
285                 self.updateSlowblank()
286                 self.updateFastblank()
287
288         def updateSlowblank(self):
289                 if self.standby:
290                         from Components.SystemInfo import SystemInfo
291                         if SystemInfo["ScartSwitch"]:
292                                 input = "scart"
293                                 sb = "vcr"
294                         else:
295                                 input = "off"
296                                 sb = "0"
297                 else:
298                         input = "encoder"
299                         sb = "auto"
300
301                 open("/proc/stb/avs/0/sb", "w").write(sb)
302                 open("/proc/stb/avs/0/input", "w").write(input)
303
304         def updateStandby(self):
305                 self.updateSlowblank()
306                 self.updateFastblank()
307
308         def updateFastblank(self, *args):
309                 if self.standby:
310                         from Components.SystemInfo import SystemInfo
311                         if SystemInfo["ScartSwitch"]:
312                                 fb = "vcr"
313                         else:
314                                 fb = "low"
315                 else:
316                         if self.current_port == "Scart" and config.av.colorformat.value == "rgb":
317                                 fb = "high"
318                         else:
319                                 fb = "low"
320                 open("/proc/stb/avs/0/fb", "w").write(fb)
321
322 config.av.edid_override = ConfigYesNo(default = False)
323 video_hw = VideoHardware()
324 video_hw.setConfiguredMode()