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