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

source code of kwindowsystem/src/kxerrorhandler.cpp