Torc  0.1
torcpowerosx.cpp
Go to the documentation of this file.
1 /* Class TorcPowerOSX
2 *
3 * This file is part of the Torc project.
4 *
5 * Copyright (C) Mark Kendall 2012-18
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
20 * USA.
21 */
22 
23 // OS X
24 #include <IOKit/pwr_mgt/IOPMLib.h>
25 #include <IOKit/ps/IOPowerSources.h>
26 #include <IOKit/ps/IOPSKeys.h>
27 
28 // Torc
29 #include "torclocalcontext.h"
30 #include "torccocoa.h"
31 #include "torcrunlooposx.h"
32 #include "torcpowerosx.h"
33 
42 static OSStatus SendAppleEventToSystemProcess(AEEventID EventToSend);
43 
45  : m_powerRef(nullptr),
46  m_rootPowerDomain(0),
47  m_powerNotifier(MACH_PORT_NULL),
48  m_powerNotifyPort(nullptr)
49 {
51 
52  if (!gAdminRunLoop)
53  {
54  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("OS X callback run loop not present - aborting"));
55  return;
56  }
57 
58  // Register for power status updates
59  m_rootPowerDomain = IORegisterForSystemPower(this, &m_powerNotifyPort, PowerCallBack, &m_powerNotifier);
60  if (m_rootPowerDomain)
61  {
62  CFRunLoopAddSource(gAdminRunLoop,
63  IONotificationPortGetRunLoopSource(m_powerNotifyPort),
64  kCFRunLoopDefaultMode);
65  }
66  else
67  {
68  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Failed to setup power status callback"));
69  }
70 
71  // Is there a battery?
72  CFArrayRef batteryinfo = nullptr;
73 
74  if (IOPMCopyBatteryInfo(kIOMasterPortDefault, &batteryinfo) == kIOReturnSuccess)
75  {
76  CFRelease(batteryinfo);
77 
78  // register for notification of power source changes
79  m_powerRef = IOPSNotificationCreateRunLoopSource(PowerSourceCallBack, this);
80  if (m_powerRef)
81  {
82  CFRunLoopAddSource(gAdminRunLoop, m_powerRef, kCFRunLoopDefaultMode);
83  Refresh();
84  }
85  }
86 
87  if (!m_powerRef)
88  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Failed to setup power source callback"));
89 
90  // Set capabilities
91  m_canShutdown->SetValue(QVariant((bool)true));
92  m_canSuspend->SetValue(QVariant((bool)IOPMSleepEnabled()));
93  m_canHibernate->SetValue(QVariant((bool)true));
94  m_canRestart->SetValue(QVariant((bool)true));
95 
96  Debug();
97 }
98 
100 {
102 
103  // deregister power status change notifications
104  if (gAdminRunLoop)
105  {
106  CFRunLoopRemoveSource(gAdminRunLoop,
107  IONotificationPortGetRunLoopSource(m_powerNotifyPort),
108  kCFRunLoopDefaultMode );
109  IODeregisterForSystemPower(&m_powerNotifier);
110  IOServiceClose(m_rootPowerDomain);
111  IONotificationPortDestroy(m_powerNotifyPort);
112  }
113 
114  // deregister power source change notifcations
115  if (m_powerRef && gAdminRunLoop)
116  {
117  CFRunLoopRemoveSource(gAdminRunLoop, m_powerRef, kCFRunLoopDefaultMode);
118  CFRelease(m_powerRef);
119  }
120 }
121 
124 {
125  m_httpServiceLock.lockForWrite();
126  OSStatus error = SendAppleEventToSystemProcess(kAEShutDown);
127  m_httpServiceLock.unlock();
128 
129  if (noErr == error)
130  {
131  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Sent shutdown command."));
133  return true;
134  }
135 
136  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Failed to send shutdown command."));
137  return false;
138 }
139 
142 {
143  m_httpServiceLock.lockForWrite();
144  OSStatus error = SendAppleEventToSystemProcess(kAESleep);
145  m_httpServiceLock.unlock();
146 
147  if (noErr == error)
148  {
149  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Sent sleep command."));
150  return true;
151  }
152 
153  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Failed to send sleep command."));
154  return false;
155 }
156 
159 {
160  return Suspend();
161 }
162 
165 {
166  m_httpServiceLock.lockForWrite();
167  OSStatus error = SendAppleEventToSystemProcess(kAERestart);
168  m_httpServiceLock.unlock();
169 
170  if (noErr == error)
171  {
172  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Sent restart command."));
173  return true;
174  }
175 
176  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Failed to send restart command."));
177  return false;
178 }
179 
186 {
187  if (m_powerRef)
188  return;
189 
190  m_httpServiceLock.lockForWrite();
191 
192  CFTypeRef info = IOPSCopyPowerSourcesInfo();
193  CFArrayRef list = IOPSCopyPowerSourcesList(info);
194 
195  for (int i = 0; i < CFArrayGetCount(list); i++)
196  {
197  CFTypeRef source = CFArrayGetValueAtIndex(list, i);
198  CFDictionaryRef description = IOPSGetPowerSourceDescription(info, source);
199 
200  if ((CFBooleanRef)CFDictionaryGetValue(description, CFSTR(kIOPSIsPresentKey)) == kCFBooleanFalse)
201  continue;
202 
203  CFStringRef type = (CFStringRef)CFDictionaryGetValue(description, CFSTR(kIOPSTransportTypeKey));
204  if (type && CFStringCompare(type, CFSTR(kIOPSInternalType), 0) == kCFCompareEqualTo)
205  {
206  CFStringRef state = (CFStringRef)CFDictionaryGetValue(description, CFSTR(kIOPSPowerSourceStateKey));
207  if (state && CFStringCompare(state, CFSTR(kIOPSACPowerValue), 0) == kCFCompareEqualTo)
208  {
210  }
211  else if (state && CFStringCompare(state, CFSTR(kIOPSBatteryPowerValue), 0) == kCFCompareEqualTo)
212  {
213  int32_t current;
214  int32_t max;
215  CFNumberRef capacity = (CFNumberRef)CFDictionaryGetValue(description, CFSTR(kIOPSCurrentCapacityKey));
216  CFNumberGetValue(capacity, kCFNumberSInt32Type, &current);
217  capacity = (CFNumberRef)CFDictionaryGetValue(description, CFSTR(kIOPSMaxCapacityKey));
218  CFNumberGetValue(capacity, kCFNumberSInt32Type, &max);
219  m_batteryLevel = (int)(((qreal)current / ((qreal)max)) * 100.0);
220  }
221  else
222  {
224  }
225  }
226  }
227 
228  CFRelease(list);
229  CFRelease(info);
230 
231  m_httpServiceLock.unlock();
232 
234 }
235 
243 void TorcPowerOSX::PowerCallBack(void *Reference, io_service_t Service,
244  natural_t Type, void *Data)
245 {
246  (void)Service;
247 
249  TorcPowerOSX* power = static_cast<TorcPowerOSX*>(Reference);
250 
251  if (power)
252  {
253  switch (Type)
254  {
255  case kIOMessageCanSystemPowerOff:
256  IOAllowPowerChange(power->m_rootPowerDomain, (long)Data);
257  power->ShuttingDown();
258  break;
259  case kIOMessageCanSystemSleep:
260  IOAllowPowerChange(power->m_rootPowerDomain, (long)Data);
261  power->Suspending();
262  break;
263  case kIOMessageSystemWillPowerOff:
264  IOAllowPowerChange(power->m_rootPowerDomain, (long)Data);
265  power->ShuttingDown();
266  break;
267  case kIOMessageSystemWillRestart:
268  IOAllowPowerChange(power->m_rootPowerDomain, (long)Data);
269  power->Restarting();
270  break;
271  case kIOMessageSystemWillSleep:
272  IOAllowPowerChange(power->m_rootPowerDomain, (long)Data);
273  power->Suspending();
274  break;
275  case kIOMessageSystemHasPoweredOn:
276  power->WokeUp();
277  break;
278  }
279  }
280 }
281 
289 {
291  TorcPowerOSX* power = static_cast<TorcPowerOSX*>(Reference);
292 
293  if (power)
294  power->Refresh();
295 }
296 
297 // see Technical Q&A QA1134
298 OSStatus SendAppleEventToSystemProcess(AEEventID EventToSend)
299 {
300  AEAddressDesc targetDesc;
301  static const ProcessSerialNumber kPSNOfSystemProcess = { 0, kSystemProcess };
302  AppleEvent eventReply = { typeNull, nullptr };
303  AppleEvent appleEventToSend = { typeNull, nullptr };
304 
305  OSStatus error = AECreateDesc(typeProcessSerialNumber, &kPSNOfSystemProcess,
306  sizeof(kPSNOfSystemProcess), &targetDesc);
307 
308  if (error != noErr)
309  return error;
310 
311  error = AECreateAppleEvent(kCoreEventClass, EventToSend, &targetDesc,
312  kAutoGenerateReturnID, kAnyTransactionID, &appleEventToSend);
313 
314  AEDisposeDesc(&targetDesc);
315  if (error != noErr)
316  return error;
317 
318  error = AESendMessage(&appleEventToSend, &eventReply, kAENormalPriority, kAEDefaultTimeout);
319 
320  AEDisposeDesc(&appleEventToSend);
321 
322  if (error != noErr)
323  return error;
324 
325  AEDisposeDesc(&eventReply);
326 
327  return error;
328 }
329 
332 {
333  void Score(int &Score)
334  {
335  if (Score <= 10)
336  Score = 10;
337  }
338 
339  TorcPower* Create(int Score)
340  {
341  if (Score <= 10)
342  return new TorcPowerOSX();
343 
344  return nullptr;
345  }
347 
A power monitoring class for OS X.
Definition: torcpowerosx.h:12
void Restarting(void)
Definition: torcpower.cpp:511
bool Suspend(void)
Definition: torcpower.cpp:340
static OSStatus SendAppleEventToSystemProcess(AEEventID EventToSend)
TorcPowerFactoryOSX TorcPowerFactoryOSX
bool DoRestart(void) override
Restart the system.
static void Create(void)
bool DoShutdown(void) override
Shutdown the system.
void BatteryUpdated(int Level)
Definition: torcpower.cpp:275
void Debug(void)
Definition: torcpower.cpp:251
TorcSetting * m_canHibernate
Definition: torcpower.h:95
CFRunLoopRef gAdminRunLoop
A reference to the global administration CFRunLoop.
static void NotifyEvent(int Event)
virtual ~TorcPowerOSX()
bool DoHibernate(void) override
Hibernate the system.
TorcSetting * m_canSuspend
Definition: torcpower.h:94
static void PowerSourceCallBack(void *Reference)
Receive notification of changes to the power supply.
void ShuttingDown(void)
Definition: torcpower.cpp:493
void Suspending(void)
Definition: torcpower.cpp:499
QReadWriteLock m_httpServiceLock
TorcSetting * m_canShutdown
Definition: torcpower.h:93
void WokeUp(void)
Definition: torcpower.cpp:517
A generic power status class.
Definition: torcpower.h:12
int m_batteryLevel
Definition: torcpower.h:97
bool SetValue(const QVariant &Value)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: torclogging.h:20
void Refresh(void)
Update the current power supply status.
bool DoSuspend(void) override
Suspend the system.
static void PowerCallBack(void *Reference, io_service_t Service, natural_t Type, void *Data)
Receive notification of power status changes.
TorcSetting * m_canRestart
Definition: torcpower.h:96
Create a TorcPowerOSX singleton to handle power status.