Merge pull request #4630 from Red-F/gotham-resume-pvr-lastplayedposition
[vuplus_xbmc] / xbmc / powermanagement / DPMSSupport.cpp
1 /*
2  *      Copyright (C) 2009-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "system.h"
22 #include "DPMSSupport.h"
23 #include "utils/log.h"
24 #include "windowing/WindowingFactory.h"
25 #include "utils/SystemInfo.h"
26 #include <assert.h>
27 #include <string>
28 #ifdef TARGET_WINDOWS
29 #include "guilib/GraphicContext.h"
30 #endif
31
32 //////// Generic, non-platform-specific code
33
34 static const char* const MODE_NAMES[DPMSSupport::NUM_MODES] =
35   { "STANDBY", "SUSPEND", "OFF" };
36
37 bool DPMSSupport::CheckValidMode(PowerSavingMode mode)
38 {
39   if (mode < 0 || mode > NUM_MODES)
40   {
41     CLog::Log(LOGERROR, "Invalid power-saving mode %d", mode);
42     return false;
43   }
44   return true;
45 }
46
47 const char* DPMSSupport::GetModeName(PowerSavingMode mode)
48 {
49   if (!CheckValidMode(mode)) return NULL;
50   return MODE_NAMES[mode];
51 }
52
53 DPMSSupport::DPMSSupport()
54 {
55   PlatformSpecificInit();
56
57   if (!m_supportedModes.empty())
58   {
59     std::string modes_message;
60     for (size_t i = 0; i < m_supportedModes.size(); i++)
61     {
62       assert(CheckValidMode(m_supportedModes[i]));
63       modes_message += " ";
64       modes_message += MODE_NAMES[m_supportedModes[i]];
65     }
66     CLog::Log(LOGDEBUG, "DPMS: supported power-saving modes:%s",
67               modes_message.c_str());
68   }
69 }
70
71 bool DPMSSupport::IsModeSupported(PowerSavingMode mode) const
72 {
73   if (!CheckValidMode(mode)) return false;
74   for (size_t i = 0; i < m_supportedModes.size(); i++)
75   {
76     if (m_supportedModes[i] == mode) return true;
77   }
78   return false;
79 }
80
81 bool DPMSSupport::EnablePowerSaving(PowerSavingMode mode)
82 {
83   if (!CheckValidMode(mode)) return false;
84   if (!IsModeSupported(mode))
85   {
86     CLog::Log(LOGERROR, "DPMS: power-saving mode %s is not supported",
87               MODE_NAMES[mode]);
88     return false;
89   }
90
91   if (!PlatformSpecificEnablePowerSaving(mode)) return false;
92
93   CLog::Log(LOGINFO, "DPMS: enabled power-saving mode %s",
94             GetModeName(mode));
95   return true;
96 }
97
98 bool DPMSSupport::DisablePowerSaving()
99 {
100   if (!PlatformSpecificDisablePowerSaving()) return false;
101   CLog::Log(LOGINFO, "DPMS: disabled power-saving");
102   return true;
103 }
104
105 ///////// Platform-specific support
106
107 #if defined(HAS_GLX)
108 //// X Windows
109
110 // Here's a sad story: our Windows-inspired BOOL type from linux/PlatformDefs.h
111 // is incompatible with the BOOL in X11's Xmd.h (int vs. unsigned char).
112 // This is not a good idea for a X11 app and it should
113 // probably be fixed. Meanwhile, we can work around it rather cleanly with
114 // the preprocessor (which is partly to blame for this needless conflict
115 // anyway). BOOL is not used in the DPMS APIs that we need. Try not to use
116 // BOOL in the remaining X11-specific code in this file, since X might
117 // someday use a #define instead of a typedef.
118 // Addendum: INT64 apparently hhas the same problem on x86_64. Oh well.
119 // Once again, INT64 is not used in the APIs we need, so we can #ifdef it away.
120 #define BOOL __X11_SPECIFIC_BOOL
121 #define INT64 __X11_SPECIFIC_INT64
122 #include <X11/Xlib.h>
123 #include <X11/extensions/dpms.h>
124 #undef INT64
125 #undef BOOL
126
127 // Mapping of PowerSavingMode to X11's mode constants.
128 static const CARD16 X_DPMS_MODES[] =
129   { DPMSModeStandby, DPMSModeSuspend, DPMSModeOff };
130
131 void DPMSSupport::PlatformSpecificInit()
132 {
133   Display* dpy = g_Windowing.GetDisplay();
134   if (dpy == NULL) return;
135
136   int event_base, error_base;   // we ignore these
137   if (!DPMSQueryExtension(dpy, &event_base, &error_base)) {
138     CLog::Log(LOGINFO, "DPMS: X11 extension not present, power-saving"
139               " will not be available");
140     return;
141   }
142
143   if (!DPMSCapable(dpy)) {
144     CLog::Log(LOGINFO, "DPMS: display does not support power-saving");
145     return;
146   }
147
148   m_supportedModes.push_back(SUSPEND); // best compromise
149   m_supportedModes.push_back(OFF);     // next best
150   m_supportedModes.push_back(STANDBY); // rather lame, < 80% power according to
151                                        // DPMS spec
152 }
153
154 bool DPMSSupport::PlatformSpecificEnablePowerSaving(PowerSavingMode mode)
155 {
156   Display* dpy = g_Windowing.GetDisplay();
157   if (dpy == NULL) return false;
158
159   // This is not needed on my ATI Radeon, but the docs say that DPMSForceLevel
160   // after a DPMSDisable (from SDL) should not normally work.
161   DPMSEnable(dpy);
162   DPMSForceLevel(dpy, X_DPMS_MODES[mode]);
163   // There shouldn't be any errors if we called DPMSEnable; if they do happen,
164   // they're asynchronous and messy to detect.
165   XFlush(dpy);
166   return true;
167 }
168
169 bool DPMSSupport::PlatformSpecificDisablePowerSaving()
170 {
171   Display* dpy = g_Windowing.GetDisplay();
172   if (dpy == NULL) return false;
173
174   DPMSForceLevel(dpy, DPMSModeOn);
175   DPMSDisable(dpy);
176   XFlush(dpy);
177   // On my ATI, the full-screen window stays blank after waking up from
178   // DPMS, presumably due to being OpenGL. There is something magical about
179   // window expose events (involving the window manager) that solves this
180   // without fail.
181   XUnmapWindow(dpy, g_Windowing.GetWindow());
182   XFlush(dpy);
183   XMapWindow(dpy, g_Windowing.GetWindow());
184   XFlush(dpy);
185
186   return true;
187 }
188
189 /////  Add other platforms here.
190 #elif defined(TARGET_WINDOWS)
191 void DPMSSupport::PlatformSpecificInit()
192 {
193   // Assume we support DPMS. Is there a way to test it?
194   m_supportedModes.push_back(OFF);
195   m_supportedModes.push_back(STANDBY);
196 }
197
198 bool DPMSSupport::PlatformSpecificEnablePowerSaving(PowerSavingMode mode)
199 {
200   if(!g_graphicsContext.IsFullScreenRoot())
201   {
202     CLog::Log(LOGDEBUG, "DPMS: not in fullscreen, power saving disabled");
203     return false;
204   }
205   switch(mode)
206   {
207   case OFF:
208     // Turn off display
209     return SendMessage(g_Windowing.GetHwnd(), WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM) 2) == 0;
210   case STANDBY:
211     // Set display to low power
212     return SendMessage(g_Windowing.GetHwnd(), WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM) 1) == 0;
213   default:
214     return true;
215   }
216 }
217
218 bool DPMSSupport::PlatformSpecificDisablePowerSaving()
219 {
220   // Turn display on
221   return SendMessage(g_Windowing.GetHwnd(), WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM) -1) == 0;
222 }
223
224 #elif defined(TARGET_DARWIN_OSX)
225 #include <IOKit/IOKitLib.h>
226 #include <CoreFoundation/CFNumber.h>
227
228 void DPMSSupport::PlatformSpecificInit()
229 {
230   m_supportedModes.push_back(OFF);
231   m_supportedModes.push_back(STANDBY);
232 }
233
234 bool DPMSSupport::PlatformSpecificEnablePowerSaving(PowerSavingMode mode)
235 {
236   bool status;
237   // http://lists.apple.com/archives/Cocoa-dev/2007/Nov/msg00267.html
238   // This is an unsupported system call that might kernel panic on PPC boxes
239   // The reported OSX-PPC panic is unverified so we are going to enable this until
240   // we find out which OSX-PPC boxes have problems, then add detect/disable for those boxes.
241   io_registry_entry_t r = IORegistryEntryFromPath(kIOMasterPortDefault, "IOService:/IOResources/IODisplayWrangler");
242   if(!r) return false;
243
244   switch(mode)
245   {
246   case OFF:
247     // Turn off display
248     status = (IORegistryEntrySetCFProperty(r, CFSTR("IORequestIdle"), kCFBooleanTrue) == 0);
249     break;
250   case STANDBY:
251     // Set display to low power
252     status = (IORegistryEntrySetCFProperty(r, CFSTR("IORequestIdle"), kCFBooleanTrue) == 0);
253     break;
254   default:
255     status = false;
256     break;
257   }
258   return status;
259 }
260
261 bool DPMSSupport::PlatformSpecificDisablePowerSaving()
262 {
263   // http://lists.apple.com/archives/Cocoa-dev/2007/Nov/msg00267.html
264   // This is an unsupported system call that might kernel panic on PPC boxes
265   // The reported OSX-PPC panic is unverified so we are going to enable this until
266   // we find out which OSX-PPC boxes have problems, then add detect/disable for those boxes.
267   io_registry_entry_t r = IORegistryEntryFromPath(kIOMasterPortDefault, "IOService:/IOResources/IODisplayWrangler");
268   if(!r) return false;
269
270   // Turn display on
271   return (IORegistryEntrySetCFProperty(r, CFSTR("IORequestIdle"), kCFBooleanFalse) == 0);
272 }
273
274 #else
275 // Not implemented on this platform
276 void DPMSSupport::PlatformSpecificInit()
277 {
278   CLog::Log(LOGINFO, "DPMS: not supported on this platform");
279 }
280
281 bool DPMSSupport::PlatformSpecificEnablePowerSaving(PowerSavingMode mode)
282 {
283   return false;
284 }
285
286 bool DPMSSupport::PlatformSpecificDisablePowerSaving()
287 {
288   return false;
289 }
290
291 #endif  // platform ifdefs
292
293