b9b5dca8661a678f4056b38029c1c88f7752272b
[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                 dvimode = (mode_50.find('x') != -1 or mode_60.find('x') != -1) and '1' or '0'
150                 try:
151                         open("/etc/dvimode", "w").write(dvimode) # use 50Hz mode (if available) for booting
152                 except IOError:
153                         print "writing initial dvimode to /etc/dvimode failed."
154
155                 try:
156                         open("/proc/stb/hdmi/enable_hdmi_reset", "w").write(dvimode)
157                 except IOError:
158                         print "setting dvimode failed."
159
160                 try:
161                         open("/proc/stb/video/videomode_50hz", "w").write(mode_50)
162                         open("/proc/stb/video/videomode_60hz", "w").write(mode_60)
163                 except IOError:
164                         try:
165                                 # fallback if no possibility to setup 50/60 hz mode
166                                 open("/proc/stb/video/videomode", "w").write(mode_50)
167                         except IOError:
168                                 print "setting videomode failed."
169
170                 try:
171                         open("/etc/videomode", "w").write(mode_50) # use 50Hz mode (if available) for booting
172                 except IOError:
173                         print "writing initial videomode to /etc/videomode failed."
174
175                 self.updateAspect(None)
176
177         def saveMode(self, port, mode, rate):
178                 config.av.videoport.value = port
179                 config.av.videoport.save()
180                 config.av.videomode[port].value = mode
181                 config.av.videomode[port].save()
182                 config.av.videorate[mode].value = rate
183                 config.av.videorate[mode].save()
184
185         def isPortAvailable(self, port):
186                 # fixme
187                 return True
188
189         def isPortUsed(self, port):
190                 if port == "DVI":
191                         self.readPreferredModes()
192                         return len(self.modes_preferred) != 0
193                 else:
194                         return True
195
196         def getPortList(self):
197                 return [port for port in self.modes if self.isPortAvailable(port)]
198
199         # get a list with all modes, with all rates, for a given port.
200         def getModeList(self, port):
201                 print "getModeList for port", port
202                 res = [ ]
203                 for mode in self.modes[port]:
204                         # list all rates which are completely valid
205                         rates = [rate for rate in self.rates[mode] if self.isModeAvailable(port, mode, rate)]
206
207                         # if at least one rate is ok, add this mode
208                         if len(rates):
209                                 res.append( (mode, rates) )
210                 return res
211
212         def createConfig(self, *args):
213                 # create list of output ports
214                 portlist = self.getPortList()
215
216                 # create list of available modes
217                 config.av.videoport = ConfigSelection(choices = [(port, _(port)) for port in portlist])
218                 config.av.videomode = ConfigSubDict()
219                 config.av.videorate = ConfigSubDict()
220
221                 for port in portlist:
222                         modes = self.getModeList(port)
223                         if len(modes):
224                                 config.av.videomode[port] = ConfigSelection(choices = [mode for (mode, rates) in modes])
225                         for (mode, rates) in modes:
226                                 config.av.videorate[mode] = ConfigSelection(choices = rates)
227
228         def setConfiguredMode(self):
229                 port = config.av.videoport.value
230                 if port not in config.av.videomode:
231                         print "current port not available, not setting videomode"
232                         return
233
234                 mode = config.av.videomode[port].value
235
236                 if mode not in config.av.videorate:
237                         print "current mode not available, not setting videomode"
238                         return
239
240                 rate = config.av.videorate[mode].value
241                 self.setMode(port, mode, rate)
242
243         def updateAspect(self, cfgelement):
244                 # determine aspect = {any,4:3,16:9,16:10}
245                 # determine policy = {bestfit,letterbox,panscan,nonlinear}
246
247                 # based on;
248                 #   config.av.videoport.value: current video output device
249                 #     Scart: 
250                 #   config.av.aspect:
251                 #     4_3:            use policy_169
252                 #     16_9,16_10:     use policy_43
253                 #     auto            always "bestfit"
254                 #   config.av.policy_169
255                 #     letterbox       use letterbox
256                 #     panscan         use panscan
257                 #     scale           use bestfit
258                 #   config.av.policy_43
259                 #     pillarbox       use panscan
260                 #     panscan         use letterbox  ("panscan" is just a bad term, it's inverse-panscan)
261                 #     nonlinear       use nonlinear
262                 #     scale           use bestfit
263
264                 port = config.av.videoport.value
265                 if port not in config.av.videomode:
266                         print "current port not available, not setting videomode"
267                         return
268                 mode = config.av.videomode[port].value
269
270                 force_widescreen = self.isWidescreenMode(port, mode)
271
272                 is_widescreen = force_widescreen or config.av.aspect.value in ["16_9", "16_10"]
273                 is_auto = config.av.aspect.value == "auto"
274
275                 if is_widescreen:
276                         if force_widescreen:
277                                 aspect = "16:9"
278                         else:
279                                 aspect = {"16_9": "16:9", "16_10": "16:10"}[config.av.aspect.value]
280                         policy = {"pillarbox": "panscan", "panscan": "letterbox", "nonlinear": "nonlinear", "scale": "bestfit"}[config.av.policy_43.value]
281                 elif is_auto:
282                         aspect = "any"
283                         policy = "bestfit"
284                 else:
285                         aspect = "4:3"
286                         policy = {"letterbox": "letterbox", "panscan": "panscan", "scale": "bestfit"}[config.av.policy_169.value]
287
288                 if not config.av.wss.value:
289                         wss = "auto(4:3_off)"
290                 else:
291                         wss = "auto"
292
293                 print "-> setting aspect, policy, wss", aspect, policy, wss
294                 open("/proc/stb/video/aspect", "w").write(aspect)
295                 open("/proc/stb/video/policy", "w").write(policy)
296                 open("/proc/stb/denc/0/wss", "w").write(wss)
297                 self.updateSlowblank()
298                 self.updateFastblank()
299
300         def updateSlowblank(self):
301                 if self.standby:
302                         from Components.SystemInfo import SystemInfo
303                         if SystemInfo["ScartSwitch"]:
304                                 input = "scart"
305                                 sb = "vcr"
306                         else:
307                                 input = "off"
308                                 sb = "0"
309                 else:
310                         input = "encoder"
311                         sb = "auto"
312
313                 open("/proc/stb/avs/0/sb", "w").write(sb)
314                 open("/proc/stb/avs/0/input", "w").write(input)
315
316         def updateStandby(self):
317                 self.updateSlowblank()
318                 self.updateFastblank()
319
320         def updateFastblank(self, *args):
321                 if self.standby:
322                         from Components.SystemInfo import SystemInfo
323                         if SystemInfo["ScartSwitch"]:
324                                 fb = "vcr"
325                         else:
326                                 fb = "low"
327                 else:
328                         if self.current_port == "Scart" and config.av.colorformat.value == "rgb":
329                                 fb = "high"
330                         else:
331                                 fb = "low"
332                 open("/proc/stb/avs/0/fb", "w").write(fb)
333
334 config.av.edid_override = ConfigYesNo(default = False)
335 video_hw = VideoHardware()
336 video_hw.setConfiguredMode()