File indexing completed on 2024-05-05 12:24:31
0001 /* 0002 SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org> 0003 0004 SPDX-License-Identifier: MIT 0005 */ 0006 0007 #include "kxerrorhandler_p.h" 0008 #include <config-kwindowsystem.h> 0009 0010 #include <fixx11h.h> 0011 0012 #include "netwm_def.h" 0013 0014 #include <stdio.h> 0015 0016 #include <QByteArray> 0017 #include <QString> 0018 0019 class KXErrorHandlerPrivate 0020 { 0021 public: 0022 KXErrorHandlerPrivate(Display *dpy) 0023 : first_request(XNextRequest(dpy)) 0024 , display(dpy) 0025 , was_error(false) 0026 { 0027 } 0028 unsigned long first_request; 0029 Display *display; 0030 bool was_error; 0031 XErrorEvent error_event; 0032 }; 0033 0034 KXErrorHandler **KXErrorHandler::handlers = nullptr; 0035 int KXErrorHandler::pos = 0; 0036 int KXErrorHandler::size = 0; 0037 0038 KXErrorHandler::KXErrorHandler(Display *dpy) 0039 : user_handler1(nullptr) 0040 , user_handler2(nullptr) 0041 , old_handler(XSetErrorHandler(handler_wrapper)) 0042 , d(new KXErrorHandlerPrivate(dpy)) 0043 { 0044 addHandler(); 0045 } 0046 0047 KXErrorHandler::KXErrorHandler(int (*handler)(Display *, XErrorEvent *), Display *dpy) 0048 : user_handler1(nullptr) 0049 , user_handler2(handler) 0050 , old_handler(XSetErrorHandler(handler_wrapper)) 0051 , d(new KXErrorHandlerPrivate(dpy)) 0052 { 0053 addHandler(); 0054 } 0055 0056 KXErrorHandler::~KXErrorHandler() 0057 { 0058 XSetErrorHandler(old_handler); 0059 Q_ASSERT_X(this == handlers[pos - 1], "KXErrorHandler", "out of order"); 0060 --pos; 0061 delete d; 0062 } 0063 0064 void KXErrorHandler::addHandler() 0065 { 0066 if (size == pos) { 0067 size += 16; 0068 handlers = static_cast<KXErrorHandler **>(realloc(handlers, size * sizeof(KXErrorHandler *))); 0069 } 0070 handlers[pos++] = this; 0071 } 0072 0073 bool KXErrorHandler::error(bool sync) const 0074 { 0075 if (sync) { 0076 XSync(d->display, False); 0077 } 0078 return d->was_error; 0079 } 0080 0081 XErrorEvent KXErrorHandler::errorEvent() const 0082 { 0083 return d->error_event; 0084 } 0085 0086 int KXErrorHandler::handler_wrapper(Display *dpy, XErrorEvent *e) 0087 { 0088 --pos; 0089 int ret = handlers[pos]->handle(dpy, e); 0090 ++pos; 0091 return ret; 0092 } 0093 0094 int KXErrorHandler::handle(Display *dpy, XErrorEvent *e) 0095 { 0096 if (dpy == d->display 0097 // e->serial >= d->first_request , compare like X timestamps to handle wrapping 0098 && NET::timestampCompare(e->serial, d->first_request) >= 0) { 0099 // it's for us 0100 // qDebug( "Handling: %p", static_cast< void* >( this )); 0101 bool error = false; 0102 if (user_handler1 != nullptr) { 0103 if (user_handler1(e->request_code, e->error_code, e->resourceid)) { 0104 error = true; 0105 } 0106 } else if (user_handler2 != nullptr) { 0107 if (user_handler2(dpy, e) != 0) { 0108 error = true; 0109 } 0110 } else { // no handler set, simply set that there was an error 0111 error = true; 0112 } 0113 if (error && !d->was_error) { 0114 // only remember the first 0115 d->was_error = true; 0116 d->error_event = *e; 0117 } 0118 return 0; 0119 } 0120 // qDebug( "Going deeper: %p", static_cast< void* >( this )); 0121 return old_handler(dpy, e); 0122 } 0123 0124 QByteArray KXErrorHandler::errorMessage(const XErrorEvent &event, Display *dpy) 0125 { 0126 // "Error: <error> (<value>), Request: <request>(<value>), Resource: <value>" 0127 QByteArray ret; 0128 char tmp[256]; 0129 #if 0 // see below 0130 char num[ 256 ]; 0131 if (event.request_code < 128) // core request 0132 #endif 0133 { 0134 XGetErrorText(dpy, event.error_code, tmp, 255); 0135 if (char *paren = strchr(tmp, '(')) { // the explanation in parentheses just makes 0136 *paren = '\0'; // it more verbose and is not really useful 0137 } 0138 // the various casts are to get overloads non-ambiguous :-/ 0139 /* 0140 ret = QByteArray("error: ") + (const char *)tmp + '[' + QByteArray::number(event.error_code) + ']'; 0141 sprintf(num, "%d", event.request_code); 0142 XGetErrorDatabaseText(dpy, "XRequest", num, "<unknown>", tmp, 256); 0143 ret += QByteArray(", request: ") + (const char *)tmp + '[' + QByteArray::number(event.request_code) + ']'; 0144 if (event.resourceid != 0) { 0145 ret += QByteArray(", resource: 0x") + QByteArray::number((qlonglong)event.resourceid, 16); 0146 } 0147 */ 0148 } 0149 #if 0 0150 else { // extensions 0151 // XGetErrorText() currently has a bug that makes it fail to find text 0152 // for some errors (when error==error_base), also XGetErrorDatabaseText() 0153 // requires the right extension name, so it is needed to get info about 0154 // all extensions. However that is almost impossible: 0155 // - Xlib itself has it, but in internal data. 0156 // - Opening another X connection now can cause deadlock with server grabs. 0157 // - Fetching it at startup means a bunch of roundtrips. 0158 // So if this becomes more useful in the future, do the roundtrips at startup, 0159 // or fetch it in kded and export as an env.var or something. 0160 Display *dpy2 = XOpenDisplay(XDisplayString(dpy)); 0161 int nextensions; 0162 char **extensions = XListExtensions(dpy2, &nextensions); 0163 int *majors = nullptr; 0164 int *error_bases = nullptr; 0165 if (extensions == nullptr) { 0166 nextensions = 0; 0167 } else { 0168 majors = new int[ nextensions ]; 0169 error_bases = new int[ nextensions ]; 0170 for (int i = 0; 0171 i < nextensions; 0172 ++i) { 0173 int dummy; 0174 if (!XQueryExtension(dpy2, extensions[ i ], &majors[ i ], &dummy, &error_bases[ i ])) { 0175 majors[ i ] = 0; 0176 error_bases[ i ] = 0; 0177 } 0178 } 0179 } 0180 XGetErrorText(dpy, event.error_code, tmp, 255); 0181 int index = -1; 0182 int base = 0; 0183 for (int i = 0; 0184 i < nextensions; 0185 ++i) 0186 if (error_bases[ i ] != 0 0187 && event.error_code >= error_bases[ i ] && (index == -1 || error_bases[ i ] > base)) { 0188 index = i; 0189 base = error_bases[ i ]; 0190 } 0191 if (tmp == QString::number(event.error_code)) { // XGetErrorText() failed, 0192 // or it has a bug that causes not finding all errors, check ourselves 0193 if (index != -1) { 0194 qsnprintf(num, 255, "%s.%d", extensions[ index ], event.error_code - base); 0195 XGetErrorDatabaseText(dpy, "XProtoError", num, "<unknown>", tmp, 255); 0196 } else { 0197 strcpy(tmp, "<unknown>"); 0198 } 0199 } 0200 if (char *paren = strchr(tmp, '(')) { 0201 *paren = '\0'; 0202 } 0203 if (index != -1) 0204 ret = QByteArray("error: ") + (const char *)tmp + '[' + (const char *)extensions[ index ] 0205 + '+' + QByteArray::number(event.error_code - base) + ']'; 0206 else { 0207 ret = QByteArray("error: ") + (const char *)tmp + '[' + QByteArray::number(event.error_code) + ']'; 0208 } 0209 tmp[ 0 ] = '\0'; 0210 for (int i = 0; 0211 i < nextensions; 0212 ++i) 0213 if (majors[ i ] == event.request_code) { 0214 qsnprintf(num, 255, "%s.%d", extensions[ i ], event.minor_code); 0215 XGetErrorDatabaseText(dpy, "XRequest", num, "<unknown>", tmp, 255); 0216 ret += QByteArray(", request: ") + (const char *)tmp + '[' + (const char *)extensions[ i ] + '+' 0217 + QByteArray::number(event.minor_code) + ']'; 0218 } 0219 if (tmp[ 0 ] == '\0') // not found??? 0220 ret += QByteArray(", request <unknown> [") + QByteArray::number(event.request_code) + ':' 0221 + QByteArray::number(event.minor_code) + ']'; 0222 if (event.resourceid != 0) { 0223 ret += QByteArray(", resource: 0x") + QByteArray::number((qlonglong)event.resourceid, 16); 0224 } 0225 if (extensions != nullptr) { 0226 XFreeExtensionList(extensions); 0227 } 0228 delete[] majors; 0229 delete[] error_bases; 0230 XCloseDisplay(dpy2); 0231 } 0232 #endif 0233 return ret; 0234 }