KIdleTime

xsyncbasedpoller.cpp
1 /* This file is part of the KDE libraries
2  SPDX-FileCopyrightText: 2009 Dario Freddi <drf at kde.org>
3 
4  SPDX-License-Identifier: LGPL-2.0-only
5 */
6 
7 // Exceptionnally, include QCoreApplication before our own header, because that one includes X11 headers (#define None...)
8 #include <QCoreApplication>
9 
10 #include "xsync_logging.h"
11 
12 #include "xsyncbasedpoller.h"
13 
14 #include <QAbstractNativeEventFilter>
15 #include <QX11Info>
16 #include <X11/Xlib-xcb.h> // XGetXCBConnection
17 #include <xcb/sync.h>
18 
19 class XSyncBasedPollerHelper : public QAbstractNativeEventFilter
20 {
21 public:
22  XSyncBasedPollerHelper() : q(nullptr), isActive(false) {}
23  ~XSyncBasedPollerHelper()
24  {
25  delete q;
26  }
27  bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override
28  {
29  Q_UNUSED(result);
30  if (isActive && eventType == "xcb_generic_event_t") {
31  q->xcbEvent(reinterpret_cast<xcb_generic_event_t *>(message));
32  }
33  return false;
34  }
35  XSyncBasedPoller *q;
36  bool isActive;
37 };
38 
39 Q_GLOBAL_STATIC(XSyncBasedPollerHelper, s_globalXSyncBasedPoller)
40 
41 XSyncBasedPoller *XSyncBasedPoller::instance()
42 {
43  if (!s_globalXSyncBasedPoller()->q) {
44  new XSyncBasedPoller;
45  }
46 
47  return s_globalXSyncBasedPoller()->q;
48 }
49 
50 XSyncBasedPoller::XSyncBasedPoller(QObject *parent)
51  : AbstractSystemPoller(parent)
52  , m_display(QX11Info::display())
53  , m_xcb_connection(nullptr)
54  , m_idleCounter(None)
55  , m_resetAlarm(None)
56  , m_available(true)
57 {
58  Q_ASSERT(!s_globalXSyncBasedPoller()->q);
59  s_globalXSyncBasedPoller()->q = this;
60 
61  if (Q_UNLIKELY(!m_display)) {
62  m_available = false;
63  qCWarning(KIDLETIME_XSYNC_PLUGIN) << "xcb sync could not find display";
64  return;
65  }
66  m_xcb_connection = XGetXCBConnection(m_display);
67 
68  QCoreApplication::instance()->installNativeEventFilter(s_globalXSyncBasedPoller());
69 
70  const xcb_query_extension_reply_t *sync_reply = xcb_get_extension_data(m_xcb_connection, &xcb_sync_id);
71  if (!sync_reply || !sync_reply->present) {
72  qCWarning(KIDLETIME_XSYNC_PLUGIN) << "xcb sync extension not found";
73  m_available = false;
74  return;
75  }
76  m_sync_event = sync_reply->first_event;
77 
78 #if 0
79 
80  // Workaround for https://bugs.freedesktop.org/show_bug.cgi?id=23403
81 #define xcb_sync_systemcounter_name(sc) (((char *) &(sc)->name_len) + 2)
82 
83  xcb_sync_list_system_counters_cookie_t cookie = xcb_sync_list_system_counters(m_xcb_connection);
84  xcb_sync_list_system_counters_reply_t *reply = xcb_sync_list_system_counters_reply(m_xcb_connection, cookie, NULL);
85 
86  xcb_sync_systemcounter_iterator_t iter;
87  for (iter = xcb_sync_list_system_counters_counters_iterator(reply);
88  iter.rem; xcb_sync_systemcounter_next(&iter)) {
89  printf("%d: %.*s\n", iter.data->counter,
90  iter.data->name_len, xcb_sync_systemcounter_name(iter.data));
91  /* Extra info for debugging: */
92  printf(" Actual name: %.*s\n", iter.data->name_len,
93  ((char *) &iter.data->name_len) + 2);
94  }
95 
96  int xcbcounters = xcb_sync_list_system_counters_counters_length(reply);
97  xcb_sync_systemcounter_iterator_t it = xcb_sync_list_system_counters_counters_iterator(reply);
98  for (int i = 0; i < xcbcounters; ++i) {
99  qCDebug(KIDLETIME_XSYNC_PLUGIN) << it.data->counter << it.rem << it.index;
100  qCDebug(KIDLETIME_XSYNC_PLUGIN) << "name length" << xcb_sync_systemcounter_name_length(it.data);
101  QByteArray name(xcb_sync_systemcounter_name(it.data), xcb_sync_systemcounter_name_length(it.data));
102  qCDebug(KIDLETIME_XSYNC_PLUGIN) << name;
103  xcb_sync_systemcounter_next(&it);
104  }
105  delete reply;
106 #endif
107 
108  int sync_major, sync_minor;
109  int old_sync_event, old_sync_error;
110  if (!XSyncQueryExtension(m_display, &old_sync_event, &old_sync_error)) {
111  m_available = false;
112  return;
113  }
114 
115  if (!XSyncInitialize(m_display, &sync_major, &sync_minor)) {
116  m_available = false;
117  return;
118  }
119 
120  int ncounters;
121  XSyncSystemCounter *counters = XSyncListSystemCounters(m_display, &ncounters);
122 
123  bool idleFound = false;
124 
125  qCDebug(KIDLETIME_XSYNC_PLUGIN) << ncounters << "counters";
126  for (int i = 0; i < ncounters; ++i) {
127  qCDebug(KIDLETIME_XSYNC_PLUGIN) << counters[i].name << counters[i].counter;
128  if (!strcmp(counters[i].name, "IDLETIME")) {
129  m_idleCounter = counters[i].counter;
130  idleFound = true;
131  break;
132  }
133  }
134 
135  XSyncFreeSystemCounterList(counters);
136 
137  if (!idleFound) {
138  m_available = false;
139  }
140 
141  if (m_available) {
142  qCDebug(KIDLETIME_XSYNC_PLUGIN) << "XSync seems available and ready";
143  } else {
144  qCDebug(KIDLETIME_XSYNC_PLUGIN) << "XSync seems not available";
145  }
146 }
147 
148 XSyncBasedPoller::~XSyncBasedPoller()
149 {
150 }
151 
152 bool XSyncBasedPoller::isAvailable()
153 {
154  return m_available;
155 }
156 
157 bool XSyncBasedPoller::setUpPoller()
158 {
159  if (!isAvailable()) {
160  return false;
161  }
162 
163  qCDebug(KIDLETIME_XSYNC_PLUGIN) << "XSync Inited";
164 
165  s_globalXSyncBasedPoller()->isActive = true;
166 
167  qCDebug(KIDLETIME_XSYNC_PLUGIN) << "Supported, init completed";
168 
169  return true;
170 }
171 
172 void XSyncBasedPoller::unloadPoller()
173 {
174  s_globalXSyncBasedPoller()->isActive = false;
175 }
176 
177 void XSyncBasedPoller::addTimeout(int nextTimeout)
178 {
179  /* We need to set the counter to the idle time + the value
180  * requested for next timeout
181  */
182 
183  // If there's already an alarm for the requested timeout, skip
184  if (m_timeoutAlarm.contains(nextTimeout)) {
185  return;
186  }
187 
188  XSyncValue timeout;
189  XSyncAlarm newalarm = None;
190 
191  XSyncIntToValue(&timeout, nextTimeout);
192 
193  setAlarm(m_display, &newalarm, m_idleCounter,
194  XSyncPositiveComparison, timeout);
195 
196  m_timeoutAlarm.insert(nextTimeout, newalarm);
197 }
198 
199 int XSyncBasedPoller::forcePollRequest()
200 {
201  return poll();
202 }
203 
204 int XSyncBasedPoller::poll()
205 {
206  XSyncValue idleTime;
207  XSyncQueryCounter(m_display, m_idleCounter, &idleTime);
208 
209  return XSyncValueLow32(idleTime);
210 }
211 
212 void XSyncBasedPoller::removeTimeout(int timeout)
213 {
214  if (m_timeoutAlarm.contains(timeout)) {
215  XSyncAlarm a = m_timeoutAlarm[timeout];
216  XSyncDestroyAlarm(m_display, a);
217  m_timeoutAlarm.remove(timeout);
218  }
219 }
220 
221 QList<int> XSyncBasedPoller::timeouts() const
222 {
223  return m_timeoutAlarm.keys();
224 }
225 
226 void XSyncBasedPoller::stopCatchingIdleEvents()
227 {
228  if (m_resetAlarm != None) {
229  XSyncDestroyAlarm(m_display, m_resetAlarm);
230  m_resetAlarm = None;
231  }
232 }
233 
234 void XSyncBasedPoller::catchIdleEvent()
235 {
236  XSyncValue idleTime;
237 
238  XSyncQueryCounter(m_display, m_idleCounter, &idleTime);
239 
240  /* Set the reset alarm to fire the next time idleCounter < the
241  * current counter value. XSyncNegativeComparison means <= so
242  * we have to subtract 1 from the counter value
243  */
244 
245  //NOTE: this must be a int, else compilation might fail
246  int overflow;
247  XSyncValue add;
248  XSyncValue plusone;
249  XSyncIntToValue(&add, -1);
250  XSyncValueAdd(&plusone, idleTime, add, &overflow);
251  setAlarm(m_display, &m_resetAlarm, m_idleCounter,
252  XSyncNegativeComparison, plusone);
253 }
254 
255 void XSyncBasedPoller::reloadAlarms()
256 {
257  XSyncValue timeout;
258 
259  for (QHash<int, XSyncAlarm>::iterator i = m_timeoutAlarm.begin(); i != m_timeoutAlarm.end(); ++i) {
260  XSyncIntToValue(&timeout, i.key());
261 
262  setAlarm(m_display, &(i.value()), m_idleCounter,
263  XSyncPositiveComparison, timeout);
264  }
265 }
266 
267 bool XSyncBasedPoller::xcbEvent(xcb_generic_event_t *event)
268 {
269  // qCDebug(KIDLETIME_XSYNC_PLUGIN) << event->response_type << "waiting for" << m_sync_event+XCB_SYNC_ALARM_NOTIFY;
270  if (event->response_type != m_sync_event + XCB_SYNC_ALARM_NOTIFY) {
271  return false;
272  }
273 
274  xcb_sync_alarm_notify_event_t *alarmEvent = reinterpret_cast<xcb_sync_alarm_notify_event_t *>(event);
275 
276  if (alarmEvent->state == XCB_SYNC_ALARMSTATE_DESTROYED) {
277  return false;
278  }
279 
280  for (QHash<int, XSyncAlarm>::const_iterator i = m_timeoutAlarm.constBegin(); i != m_timeoutAlarm.constEnd(); ++i) {
281  if (alarmEvent->alarm == i.value()) {
282  /* Bling! Caught! */
283  emit timeoutReached(i.key());
284  // Update the alarm to fire back if the system gets inactive for the same time
285  catchIdleEvent();
286  return false;
287  }
288  }
289 
290  if (alarmEvent->alarm == m_resetAlarm) {
291  /* Resuming from idle here! */
292  stopCatchingIdleEvents();
293  reloadAlarms();
294  emit resumingFromIdle();
295  }
296 
297  return false;
298 }
299 
300 void XSyncBasedPoller::setAlarm(Display *dpy, XSyncAlarm *alarm, XSyncCounter counter,
301  XSyncTestType test, XSyncValue value)
302 {
303  XSyncAlarmAttributes attr;
304  XSyncValue delta;
305  unsigned int flags;
306 
307  XSyncIntToValue(&delta, 0);
308 
309  attr.trigger.counter = counter;
310  attr.trigger.value_type = XSyncAbsolute;
311  attr.trigger.test_type = test;
312  attr.trigger.wait_value = value;
313  attr.delta = delta;
314 
315  flags = XSyncCACounter | XSyncCAValueType | XSyncCATestType |
316  XSyncCAValue | XSyncCADelta;
317 
318  if (*alarm) {
319  //xcb_sync_change_alarm_checked(m_xcb_connection, alarmId, ...
320  XSyncChangeAlarm(dpy, *alarm, flags, &attr);
321  } else {
322  *alarm = XSyncCreateAlarm(dpy, flags, &attr);
323  qCDebug(KIDLETIME_XSYNC_PLUGIN) << "Created alarm" << *alarm;
324  }
325 
326  XFlush(m_display);
327 }
328 
329 void XSyncBasedPoller::simulateUserActivity()
330 {
331  XResetScreenSaver(m_display);
332  XFlush(m_display);
333 }
334 
QString name(const QVariant &location)
KIOFILEWIDGETS_EXPORT void add(const QString &fileClass, const QString &directory)
virtual bool event(QEvent *e)
virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *result)=0
QHash::iterator begin()
void timeoutReached(int identifier)
Triggered when the system has been idle for x milliseconds, identified by the previously set timeout...
QCoreApplication * instance()
void installNativeEventFilter(QAbstractNativeEventFilter *filterObj)
QHash::const_iterator constBegin() const const
int idleTime() const
Retrieves the idle time of the system, in milliseconds.
Definition: kidletime.cpp:259
void resumingFromIdle()
Triggered, if KIdleTime is catching resume events, when the system resumes from an idle state...
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Thu Jul 9 2020 22:48:54 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.