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