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