KWindowSystem

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

KDE's Doxygen guidelines are available online.