File indexing completed on 2024-04-28 03:54:38

0001 /* This file is part of the KDE libraries
0002  * SPDX-FileCopyrightText: 2009 Dario Freddi <drf at kde.org>
0003  * SPDX-FileCopyrightText: 2003 Tarkvara Design Inc. (from KVIrc source code)
0004  * SPDX-FileCopyrightText: 2008 Roman Jarosz <kedgedev at centrum.cz>
0005  * SPDX-FileCopyrightText: 2008 the Kopete developers <kopete-devel at kde.org>
0006  *
0007  * SPDX-License-Identifier: LGPL-2.1-or-later
0008  */
0009 
0010 #include "macpoller.h"
0011 
0012 // Why does Apple have to make this so complicated?
0013 static OSStatus LoadFrameworkBundle(CFStringRef framework, CFBundleRef *bundlePtr)
0014 {
0015     OSStatus err;
0016     FSRef frameworksFolderRef;
0017     CFURLRef baseURL;
0018     CFURLRef bundleURL;
0019 
0020     if (bundlePtr == nil) {
0021         return (-1);
0022     }
0023 
0024     *bundlePtr = nil;
0025 
0026     baseURL = nil;
0027     bundleURL = nil;
0028 
0029     err = FSFindFolder(kOnAppropriateDisk, kFrameworksFolderType, true, &frameworksFolderRef);
0030     if (err == noErr) {
0031         baseURL = CFURLCreateFromFSRef(kCFAllocatorSystemDefault, &frameworksFolderRef);
0032         if (baseURL == nil) {
0033             err = coreFoundationUnknownErr;
0034         }
0035     }
0036 
0037     if (err == noErr) {
0038         bundleURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, framework, false);
0039         if (bundleURL == nil) {
0040             err = coreFoundationUnknownErr;
0041         }
0042     }
0043 
0044     if (err == noErr) {
0045         *bundlePtr = CFBundleCreate(kCFAllocatorSystemDefault, bundleURL);
0046         if (*bundlePtr == nil) {
0047             err = coreFoundationUnknownErr;
0048         }
0049     }
0050 
0051     if (err == noErr) {
0052         if (!CFBundleLoadExecutable(*bundlePtr)) {
0053             err = coreFoundationUnknownErr;
0054         }
0055     }
0056 
0057     // Clean up.
0058     if (err != noErr && *bundlePtr != nil) {
0059         CFRelease(*bundlePtr);
0060         *bundlePtr = nil;
0061     }
0062 
0063     if (bundleURL != nil) {
0064         CFRelease(bundleURL);
0065     }
0066 
0067     if (baseURL != nil) {
0068         CFRelease(baseURL);
0069     }
0070 
0071     return err;
0072 }
0073 
0074 pascal void MacPoller::IdleTimerAction(EventLoopTimerRef, EventLoopIdleTimerMessage inState, void *inUserData)
0075 {
0076     Q_ASSERT(inUserData);
0077     switch (inState) {
0078     case kEventLoopIdleTimerStarted:
0079     case kEventLoopIdleTimerStopped:
0080         // Get invoked with this constant at the start of the idle period,
0081         // or whenever user activity cancels the idle.
0082         ((MacPoller *)inUserData)->m_secondsIdle = 0;
0083         ((MacPoller *)inUserData)->triggerResume();
0084         break;
0085     case kEventLoopIdleTimerIdling:
0086         // Called every time the timer fires (i.e. every second).
0087         ((MacPoller *)inUserData)->m_secondsIdle++;
0088         ((MacPoller *)inUserData)->poll();
0089         break;
0090     }
0091 }
0092 
0093 // Typedef for the function we're getting back from CFBundleGetFunctionPointerForName.
0094 typedef OSStatus (*InstallEventLoopIdleTimerPtr)(EventLoopRef inEventLoop,
0095                                                  EventTimerInterval inFireDelay,
0096                                                  EventTimerInterval inInterval,
0097                                                  EventLoopIdleTimerUPP inTimerProc,
0098                                                  void *inTimerData,
0099                                                  EventLoopTimerRef *outTimer);
0100 
0101 MacPoller::MacPoller(QObject *parent)
0102     : KAbstractIdleTimePoller(parent)
0103     , m_timerRef(0)
0104     , m_secondsIdle(0)
0105     , m_catch(false)
0106 {
0107 }
0108 
0109 MacPoller::~MacPoller()
0110 {
0111 }
0112 
0113 void MacPoller::unloadPoller()
0114 {
0115     RemoveEventLoopTimer(m_timerRef);
0116 }
0117 
0118 bool MacPoller::isAvailable()
0119 {
0120     return true;
0121 }
0122 
0123 bool MacPoller::setUpPoller()
0124 {
0125     // May already be init'ed.
0126     if (m_timerRef) {
0127         return true;
0128     }
0129 
0130     // According to the docs, InstallEventLoopIdleTimer is new in 10.2.
0131     // According to the headers, it has been around since 10.0.
0132     // One of them is lying.  We'll play it safe and weak-link the function.
0133 
0134     // Load the "Carbon.framework" bundle.
0135     CFBundleRef carbonBundle;
0136 
0137     if (LoadFrameworkBundle(CFSTR("Carbon.framework"), &carbonBundle) != noErr) {
0138         return false;
0139     }
0140 
0141     // Load the Mach-O function pointers for the routine we will be using.
0142     InstallEventLoopIdleTimerPtr myInstallEventLoopIdleTimer =
0143         (InstallEventLoopIdleTimerPtr)CFBundleGetFunctionPointerForName(carbonBundle, CFSTR("InstallEventLoopIdleTimer"));
0144 
0145     if (myInstallEventLoopIdleTimer == 0) {
0146         return false;
0147     }
0148 
0149     EventLoopIdleTimerUPP timerUPP = NewEventLoopIdleTimerUPP(IdleTimerAction);
0150     if ((*myInstallEventLoopIdleTimer)(GetMainEventLoop(), kEventDurationSecond, kEventDurationSecond, timerUPP, this, &m_timerRef)) {
0151         return true;
0152     }
0153 
0154     return false;
0155 }
0156 
0157 QList<int> MacPoller::timeouts() const
0158 {
0159     return m_timeouts;
0160 }
0161 
0162 void MacPoller::addTimeout(int nextTimeout)
0163 {
0164     m_timeouts.append(nextTimeout);
0165     poll();
0166 }
0167 
0168 int MacPoller::poll()
0169 {
0170     int idle = m_secondsIdle * 1000;
0171 
0172     // Check if we reached a timeout..
0173     for (int i : std::as_const(m_timeouts)) {
0174         if ((i - idle < 1000 && i > idle) || (idle - i < 1000 && idle > i)) {
0175             // Bingo!
0176             Q_EMIT timeoutReached(i);
0177         }
0178     }
0179 
0180     return idle;
0181 }
0182 
0183 int MacPoller::forcePollRequest()
0184 {
0185     return poll();
0186 }
0187 
0188 void MacPoller::removeTimeout(int timeout)
0189 {
0190     m_timeouts.removeOne(timeout);
0191     poll();
0192 }
0193 
0194 void MacPoller::catchIdleEvent()
0195 {
0196     m_catch = true;
0197 }
0198 
0199 void MacPoller::stopCatchingIdleEvents()
0200 {
0201     m_catch = false;
0202 }
0203 
0204 void MacPoller::triggerResume()
0205 {
0206     if (m_catch) {
0207         Q_EMIT resumingFromIdle();
0208         stopCatchingIdleEvents();
0209     }
0210 }
0211 
0212 void MacPoller::simulateUserActivity()
0213 {
0214     // TODO
0215 }
0216 
0217 #include "moc_macpoller.cpp"