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 0004 SPDX-License-Identifier: LGPL-2.1-or-later 0005 */ 0006 0007 // Exceptionnally, include QCoreApplication before our own header, because that one includes X11 headers (#define None...) 0008 #include <QCoreApplication> 0009 0010 #include "xsync_logging.h" 0011 0012 #include "xsyncbasedpoller.h" 0013 0014 #include <QAbstractNativeEventFilter> 0015 #include <QGuiApplication> 0016 0017 #include <X11/Xlib-xcb.h> // XGetXCBConnection 0018 #include <xcb/sync.h> 0019 0020 class XSyncBasedPollerHelper : public QAbstractNativeEventFilter 0021 { 0022 public: 0023 XSyncBasedPollerHelper() 0024 : q(nullptr) 0025 , isActive(false) 0026 { 0027 } 0028 ~XSyncBasedPollerHelper() override 0029 { 0030 delete q; 0031 } 0032 bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override 0033 { 0034 Q_UNUSED(result); 0035 if (isActive && eventType == "xcb_generic_event_t") { 0036 q->xcbEvent(reinterpret_cast<xcb_generic_event_t *>(message)); 0037 } 0038 return false; 0039 } 0040 XSyncBasedPoller *q; 0041 bool isActive; 0042 }; 0043 0044 Q_GLOBAL_STATIC(XSyncBasedPollerHelper, s_globalXSyncBasedPoller) 0045 0046 XSyncBasedPoller *XSyncBasedPoller::instance() 0047 { 0048 if (!s_globalXSyncBasedPoller()->q) { 0049 new XSyncBasedPoller; 0050 } 0051 0052 return s_globalXSyncBasedPoller()->q; 0053 } 0054 0055 XSyncBasedPoller::XSyncBasedPoller(QObject *parent) 0056 : KAbstractIdleTimePoller(parent) 0057 , m_display(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display()) 0058 , m_xcb_connection(nullptr) 0059 , m_sync_event(0) 0060 , m_idleCounter(None) 0061 , m_resetAlarm(None) 0062 , m_available(true) 0063 { 0064 Q_ASSERT(!s_globalXSyncBasedPoller()->q); 0065 s_globalXSyncBasedPoller()->q = this; 0066 0067 if (Q_UNLIKELY(!m_display)) { 0068 m_available = false; 0069 qCWarning(KIDLETIME_XSYNC_PLUGIN) << "xcb sync could not find display"; 0070 return; 0071 } 0072 m_xcb_connection = XGetXCBConnection(m_display); 0073 0074 QCoreApplication::instance()->installNativeEventFilter(s_globalXSyncBasedPoller()); 0075 0076 const xcb_query_extension_reply_t *sync_reply = xcb_get_extension_data(m_xcb_connection, &xcb_sync_id); 0077 if (!sync_reply || !sync_reply->present) { 0078 qCWarning(KIDLETIME_XSYNC_PLUGIN) << "xcb sync extension not found"; 0079 m_available = false; 0080 return; 0081 } 0082 m_sync_event = sync_reply->first_event; 0083 0084 #if 0 0085 0086 // Workaround for https://bugs.freedesktop.org/show_bug.cgi?id=23403 0087 #define xcb_sync_systemcounter_name(sc) (((char *)&(sc)->name_len) + 2) 0088 0089 xcb_sync_list_system_counters_cookie_t cookie = xcb_sync_list_system_counters(m_xcb_connection); 0090 xcb_sync_list_system_counters_reply_t *reply = xcb_sync_list_system_counters_reply(m_xcb_connection, cookie, NULL); 0091 0092 xcb_sync_systemcounter_iterator_t iter; 0093 for (iter = xcb_sync_list_system_counters_counters_iterator(reply); 0094 iter.rem; xcb_sync_systemcounter_next(&iter)) { 0095 printf("%d: %.*s\n", iter.data->counter, 0096 iter.data->name_len, xcb_sync_systemcounter_name(iter.data)); 0097 /* Extra info for debugging: */ 0098 printf(" Actual name: %.*s\n", iter.data->name_len, 0099 ((char *) &iter.data->name_len) + 2); 0100 } 0101 0102 int xcbcounters = xcb_sync_list_system_counters_counters_length(reply); 0103 xcb_sync_systemcounter_iterator_t it = xcb_sync_list_system_counters_counters_iterator(reply); 0104 for (int i = 0; i < xcbcounters; ++i) { 0105 qCDebug(KIDLETIME_XSYNC_PLUGIN) << it.data->counter << it.rem << it.index; 0106 qCDebug(KIDLETIME_XSYNC_PLUGIN) << "name length" << xcb_sync_systemcounter_name_length(it.data); 0107 QByteArray name(xcb_sync_systemcounter_name(it.data), xcb_sync_systemcounter_name_length(it.data)); 0108 qCDebug(KIDLETIME_XSYNC_PLUGIN) << name; 0109 xcb_sync_systemcounter_next(&it); 0110 } 0111 delete reply; 0112 #endif 0113 0114 int sync_major; 0115 int sync_minor; 0116 int old_sync_event; 0117 int old_sync_error; 0118 if (!XSyncQueryExtension(m_display, &old_sync_event, &old_sync_error)) { 0119 m_available = false; 0120 return; 0121 } 0122 0123 if (!XSyncInitialize(m_display, &sync_major, &sync_minor)) { 0124 m_available = false; 0125 return; 0126 } 0127 0128 int ncounters; 0129 XSyncSystemCounter *counters = XSyncListSystemCounters(m_display, &ncounters); 0130 0131 bool idleFound = false; 0132 0133 qCDebug(KIDLETIME_XSYNC_PLUGIN) << ncounters << "counters"; 0134 for (int i = 0; i < ncounters; ++i) { 0135 qCDebug(KIDLETIME_XSYNC_PLUGIN) << counters[i].name << counters[i].counter; 0136 if (!strcmp(counters[i].name, "IDLETIME")) { 0137 m_idleCounter = counters[i].counter; 0138 idleFound = true; 0139 break; 0140 } 0141 } 0142 0143 XSyncFreeSystemCounterList(counters); 0144 0145 if (!idleFound) { 0146 m_available = false; 0147 } 0148 0149 if (m_available) { 0150 qCDebug(KIDLETIME_XSYNC_PLUGIN) << "XSync seems available and ready"; 0151 } else { 0152 qCDebug(KIDLETIME_XSYNC_PLUGIN) << "XSync seems not available"; 0153 } 0154 } 0155 0156 XSyncBasedPoller::~XSyncBasedPoller() 0157 { 0158 } 0159 0160 bool XSyncBasedPoller::isAvailable() 0161 { 0162 return m_available; 0163 } 0164 0165 bool XSyncBasedPoller::setUpPoller() 0166 { 0167 if (!isAvailable()) { 0168 return false; 0169 } 0170 0171 qCDebug(KIDLETIME_XSYNC_PLUGIN) << "XSync Inited"; 0172 0173 s_globalXSyncBasedPoller()->isActive = true; 0174 0175 qCDebug(KIDLETIME_XSYNC_PLUGIN) << "Supported, init completed"; 0176 0177 return true; 0178 } 0179 0180 void XSyncBasedPoller::unloadPoller() 0181 { 0182 s_globalXSyncBasedPoller()->isActive = false; 0183 } 0184 0185 void XSyncBasedPoller::addTimeout(int nextTimeout) 0186 { 0187 /* We need to set the counter to the idle time + the value 0188 * requested for next timeout 0189 */ 0190 0191 // If there's already an alarm for the requested timeout, skip 0192 if (m_timeoutAlarm.contains(nextTimeout)) { 0193 return; 0194 } 0195 0196 XSyncValue timeout; 0197 XSyncAlarm newalarm = None; 0198 0199 XSyncIntToValue(&timeout, nextTimeout); 0200 0201 setAlarm(m_display, &newalarm, m_idleCounter, XSyncPositiveComparison, timeout); 0202 0203 m_timeoutAlarm.insert(nextTimeout, newalarm); 0204 } 0205 0206 int XSyncBasedPoller::forcePollRequest() 0207 { 0208 return poll(); 0209 } 0210 0211 int XSyncBasedPoller::poll() 0212 { 0213 XSyncValue idleTime; 0214 XSyncQueryCounter(m_display, m_idleCounter, &idleTime); 0215 0216 return XSyncValueLow32(idleTime); 0217 } 0218 0219 void XSyncBasedPoller::removeTimeout(int timeout) 0220 { 0221 if (m_timeoutAlarm.contains(timeout)) { 0222 XSyncAlarm a = m_timeoutAlarm[timeout]; 0223 XSyncDestroyAlarm(m_display, a); 0224 m_timeoutAlarm.remove(timeout); 0225 } 0226 } 0227 0228 QList<int> XSyncBasedPoller::timeouts() const 0229 { 0230 return m_timeoutAlarm.keys(); 0231 } 0232 0233 void XSyncBasedPoller::stopCatchingIdleEvents() 0234 { 0235 if (m_resetAlarm != None) { 0236 XSyncDestroyAlarm(m_display, m_resetAlarm); 0237 m_resetAlarm = None; 0238 } 0239 } 0240 0241 void XSyncBasedPoller::catchIdleEvent() 0242 { 0243 XSyncValue idleTime; 0244 0245 XSyncQueryCounter(m_display, m_idleCounter, &idleTime); 0246 0247 /* Set the reset alarm to fire the next time idleCounter < the 0248 * current counter value. XSyncNegativeComparison means <= so 0249 * we have to subtract 1 from the counter value 0250 */ 0251 0252 // NOTE: this must be a int, else compilation might fail 0253 int overflow; 0254 XSyncValue add; 0255 XSyncValue plusone; 0256 XSyncIntToValue(&add, -1); 0257 XSyncValueAdd(&plusone, idleTime, add, &overflow); 0258 setAlarm(m_display, &m_resetAlarm, m_idleCounter, XSyncNegativeComparison, plusone); 0259 } 0260 0261 void XSyncBasedPoller::reloadAlarms() 0262 { 0263 XSyncValue timeout; 0264 0265 for (QHash<int, XSyncAlarm>::iterator i = m_timeoutAlarm.begin(); i != m_timeoutAlarm.end(); ++i) { 0266 XSyncIntToValue(&timeout, i.key()); 0267 0268 setAlarm(m_display, &(i.value()), m_idleCounter, XSyncPositiveComparison, timeout); 0269 } 0270 } 0271 0272 bool XSyncBasedPoller::xcbEvent(xcb_generic_event_t *event) 0273 { 0274 // qCDebug(KIDLETIME_XSYNC_PLUGIN) << event->response_type << "waiting for" << m_sync_event+XCB_SYNC_ALARM_NOTIFY; 0275 if (event->response_type != m_sync_event + XCB_SYNC_ALARM_NOTIFY) { 0276 return false; 0277 } 0278 0279 xcb_sync_alarm_notify_event_t *alarmEvent = reinterpret_cast<xcb_sync_alarm_notify_event_t *>(event); 0280 0281 if (alarmEvent->state == XCB_SYNC_ALARMSTATE_DESTROYED) { 0282 return false; 0283 } 0284 0285 for (QHash<int, XSyncAlarm>::const_iterator i = m_timeoutAlarm.constBegin(); i != m_timeoutAlarm.constEnd(); ++i) { 0286 if (alarmEvent->alarm == i.value()) { 0287 /* Bling! Caught! */ 0288 Q_EMIT timeoutReached(i.key()); 0289 // Update the alarm to fire back if the system gets inactive for the same time 0290 catchIdleEvent(); 0291 return false; 0292 } 0293 } 0294 0295 if (alarmEvent->alarm == m_resetAlarm) { 0296 /* Resuming from idle here! */ 0297 stopCatchingIdleEvents(); 0298 reloadAlarms(); 0299 Q_EMIT resumingFromIdle(); 0300 } 0301 0302 return false; 0303 } 0304 0305 void XSyncBasedPoller::setAlarm(Display *dpy, XSyncAlarm *alarm, XSyncCounter counter, XSyncTestType test, XSyncValue value) 0306 { 0307 XSyncAlarmAttributes attr; 0308 XSyncValue delta; 0309 unsigned int flags; 0310 0311 XSyncIntToValue(&delta, 0); 0312 0313 attr.trigger.counter = counter; 0314 attr.trigger.value_type = XSyncAbsolute; 0315 attr.trigger.test_type = test; 0316 attr.trigger.wait_value = value; 0317 attr.delta = delta; 0318 0319 flags = XSyncCACounter | XSyncCAValueType | XSyncCATestType | XSyncCAValue | XSyncCADelta; 0320 0321 if (*alarm) { 0322 // xcb_sync_change_alarm_checked(m_xcb_connection, alarmId, ... 0323 XSyncChangeAlarm(dpy, *alarm, flags, &attr); 0324 } else { 0325 *alarm = XSyncCreateAlarm(dpy, flags, &attr); 0326 qCDebug(KIDLETIME_XSYNC_PLUGIN) << "Created alarm" << *alarm; 0327 } 0328 0329 XFlush(m_display); 0330 } 0331 0332 void XSyncBasedPoller::simulateUserActivity() 0333 { 0334 XResetScreenSaver(m_display); 0335 XFlush(m_display); 0336 } 0337 0338 #include "moc_xsyncbasedpoller.cpp"