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

KDE's Doxygen guidelines are available online.