KWindowSystem

kxerrorhandler.cpp
1 /*
2  SPDX-FileCopyrightText: 2003 Lubos Lunak <[email protected]>
3 
4  SPDX-License-Identifier: MIT
5 */
6 
7 #include "kxerrorhandler_p.h"
8 #include <config-kwindowsystem.h>
9 
10 #include "netwm_def.h"
11 
12 #include <stdio.h>
13 
14 #include <QByteArray>
15 #include <QString>
16 
17 class KXErrorHandlerPrivate
18 {
19 public:
20  KXErrorHandlerPrivate(Display *dpy)
21  : first_request(XNextRequest(dpy))
22  , display(dpy)
23  , was_error(false)
24  {
25  }
26  unsigned long first_request;
27  Display *display;
28  bool was_error;
29  XErrorEvent error_event;
30 };
31 
32 KXErrorHandler **KXErrorHandler::handlers = nullptr;
33 int KXErrorHandler::pos = 0;
34 int KXErrorHandler::size = 0;
35 
37  : user_handler1(nullptr)
38  , user_handler2(nullptr)
39  , old_handler(XSetErrorHandler(handler_wrapper))
40  , d(new KXErrorHandlerPrivate(dpy))
41 {
42  addHandler();
43 }
44 
45 KXErrorHandler::KXErrorHandler(int (*handler)(Display *, XErrorEvent *), Display *dpy)
46  : user_handler1(nullptr)
47  , user_handler2(handler)
48  , old_handler(XSetErrorHandler(handler_wrapper))
49  , d(new KXErrorHandlerPrivate(dpy))
50 {
51  addHandler();
52 }
53 
54 KXErrorHandler::~KXErrorHandler()
55 {
56  XSetErrorHandler(old_handler);
57  Q_ASSERT_X(this == handlers[pos - 1], "KXErrorHandler", "out of order");
58  --pos;
59  delete d;
60 }
61 
62 void KXErrorHandler::addHandler()
63 {
64  if (size == pos) {
65  size += 16;
66  handlers = static_cast<KXErrorHandler **>(realloc(handlers, size * sizeof(KXErrorHandler *)));
67  }
68  handlers[pos++] = this;
69 }
70 
71 bool KXErrorHandler::error(bool sync) const
72 {
73  if (sync) {
74  XSync(d->display, False);
75  }
76  return d->was_error;
77 }
78 
79 XErrorEvent KXErrorHandler::errorEvent() const
80 {
81  return d->error_event;
82 }
83 
84 int KXErrorHandler::handler_wrapper(Display *dpy, XErrorEvent *e)
85 {
86  --pos;
87  int ret = handlers[pos]->handle(dpy, e);
88  ++pos;
89  return ret;
90 }
91 
92 int KXErrorHandler::handle(Display *dpy, XErrorEvent *e)
93 {
94  if (dpy == d->display
95  // e->serial >= d->first_request , compare like X timestamps to handle wrapping
96  && NET::timestampCompare(e->serial, d->first_request) >= 0) {
97  // it's for us
98  // qDebug( "Handling: %p", static_cast< void* >( this ));
99  bool error = false;
100  if (user_handler1 != nullptr) {
101  if (user_handler1(e->request_code, e->error_code, e->resourceid)) {
102  error = true;
103  }
104  } else if (user_handler2 != nullptr) {
105  if (user_handler2(dpy, e) != 0) {
106  error = true;
107  }
108  } else { // no handler set, simply set that there was an error
109  error = true;
110  }
111  if (error && !d->was_error) {
112  // only remember the first
113  d->was_error = true;
114  d->error_event = *e;
115  }
116  return 0;
117  }
118  // qDebug( "Going deeper: %p", static_cast< void* >( this ));
119  return old_handler(dpy, e);
120 }
121 
122 QByteArray KXErrorHandler::errorMessage(const XErrorEvent &event, Display *dpy)
123 {
124  // "Error: <error> (<value>), Request: <request>(<value>), Resource: <value>"
125  QByteArray ret;
126  char tmp[256];
127 #if 0 // see below
128  char num[ 256 ];
129  if (event.request_code < 128) // core request
130 #endif
131  {
132  XGetErrorText(dpy, event.error_code, tmp, 255);
133  if (char *paren = strchr(tmp, '(')) { // the explanation in parentheses just makes
134  *paren = '\0'; // it more verbose and is not really useful
135  }
136  // the various casts are to get overloads non-ambiguous :-/
137  /*
138  ret = QByteArray("error: ") + (const char *)tmp + '[' + QByteArray::number(event.error_code) + ']';
139  sprintf(num, "%d", event.request_code);
140  XGetErrorDatabaseText(dpy, "XRequest", num, "<unknown>", tmp, 256);
141  ret += QByteArray(", request: ") + (const char *)tmp + '[' + QByteArray::number(event.request_code) + ']';
142  if (event.resourceid != 0) {
143  ret += QByteArray(", resource: 0x") + QByteArray::number((qlonglong)event.resourceid, 16);
144  }
145  */
146  }
147 #if 0
148  else { // extensions
149  // XGetErrorText() currently has a bug that makes it fail to find text
150  // for some errors (when error==error_base), also XGetErrorDatabaseText()
151  // requires the right extension name, so it is needed to get info about
152  // all extensions. However that is almost impossible:
153  // - Xlib itself has it, but in internal data.
154  // - Opening another X connection now can cause deadlock with server grabs.
155  // - Fetching it at startup means a bunch of roundtrips.
156  // So if this becomes more useful in the future, do the roundtrips at startup,
157  // or fetch it in kded and export as an env.var or something.
158  Display *dpy2 = XOpenDisplay(XDisplayString(dpy));
159  int nextensions;
160  char **extensions = XListExtensions(dpy2, &nextensions);
161  int *majors = nullptr;
162  int *error_bases = nullptr;
163  if (extensions == nullptr) {
164  nextensions = 0;
165  } else {
166  majors = new int[ nextensions ];
167  error_bases = new int[ nextensions ];
168  for (int i = 0;
169  i < nextensions;
170  ++i) {
171  int dummy;
172  if (!XQueryExtension(dpy2, extensions[ i ], &majors[ i ], &dummy, &error_bases[ i ])) {
173  majors[ i ] = 0;
174  error_bases[ i ] = 0;
175  }
176  }
177  }
178  XGetErrorText(dpy, event.error_code, tmp, 255);
179  int index = -1;
180  int base = 0;
181  for (int i = 0;
182  i < nextensions;
183  ++i)
184  if (error_bases[ i ] != 0
185  && event.error_code >= error_bases[ i ] && (index == -1 || error_bases[ i ] > base)) {
186  index = i;
187  base = error_bases[ i ];
188  }
189  if (tmp == QString::number(event.error_code)) { // XGetErrorText() failed,
190  // or it has a bug that causes not finding all errors, check ourselves
191  if (index != -1) {
192  qsnprintf(num, 255, "%s.%d", extensions[ index ], event.error_code - base);
193  XGetErrorDatabaseText(dpy, "XProtoError", num, "<unknown>", tmp, 255);
194  } else {
195  strcpy(tmp, "<unknown>");
196  }
197  }
198  if (char *paren = strchr(tmp, '(')) {
199  *paren = '\0';
200  }
201  if (index != -1)
202  ret = QByteArray("error: ") + (const char *)tmp + '[' + (const char *)extensions[ index ]
203  + '+' + QByteArray::number(event.error_code - base) + ']';
204  else {
205  ret = QByteArray("error: ") + (const char *)tmp + '[' + QByteArray::number(event.error_code) + ']';
206  }
207  tmp[ 0 ] = '\0';
208  for (int i = 0;
209  i < nextensions;
210  ++i)
211  if (majors[ i ] == event.request_code) {
212  qsnprintf(num, 255, "%s.%d", extensions[ i ], event.minor_code);
213  XGetErrorDatabaseText(dpy, "XRequest", num, "<unknown>", tmp, 255);
214  ret += QByteArray(", request: ") + (const char *)tmp + '[' + (const char *)extensions[ i ] + '+'
215  + QByteArray::number(event.minor_code) + ']';
216  }
217  if (tmp[ 0 ] == '\0') // not found???
218  ret += QByteArray(", request <unknown> [") + QByteArray::number(event.request_code) + ':'
219  + QByteArray::number(event.minor_code) + ']';
220  if (event.resourceid != 0) {
221  ret += QByteArray(", resource: 0x") + QByteArray::number((qlonglong)event.resourceid, 16);
222  }
223  if (extensions != nullptr) {
224  XFreeExtensionList(extensions);
225  }
226  delete[] majors;
227  delete[] error_bases;
228  XCloseDisplay(dpy2);
229  }
230 #endif
231  return ret;
232 }
bool error(bool sync) const
XErrorEvent errorEvent() const
KXErrorHandler(Display *dpy=display())
virtual bool event(QEvent *e)
static int timestampCompare(unsigned long time1, unsigned long time2)
Compares two X timestamps, taking into account wrapping and 64bit architectures.
Definition: netwm.cpp:4938
QString number(int n, int base)
QByteArray number(int n, int base)
void error(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Notify)
static QByteArray errorMessage(const XErrorEvent &e, Display *dpy=display())
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Fri Oct 15 2021 22:41:50 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.