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