File indexing completed on 2024-04-14 14:20:26
0001 /* 0002 0003 Copyright (c) 2003 Lubos Lunak <l.lunak@kde.org> 0004 0005 Permission is hereby granted, free of charge, to any person obtaining a 0006 copy of this software and associated documentation files (the "Software"), 0007 to deal in the Software without restriction, including without limitation 0008 the rights to use, copy, modify, merge, publish, distribute, sublicense, 0009 and/or sell copies of the Software, and to permit persons to whom the 0010 Software is furnished to do so, subject to the following conditions: 0011 0012 The above copyright notice and this permission notice shall be included in 0013 all copies or substantial portions of the Software. 0014 0015 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 0016 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 0017 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 0018 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 0019 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 0020 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 0021 DEALINGS IN THE SOFTWARE. 0022 0023 */ 0024 0025 #include "kxerrorhandler.h" 0026 0027 #include "netwm_def.h" 0028 0029 #include <stdio.h> 0030 #include <QX11Info> 0031 0032 class KXErrorHandlerPrivate 0033 { 0034 public: 0035 KXErrorHandlerPrivate(Display *dpy) : 0036 first_request(XNextRequest(dpy)), 0037 display(dpy), 0038 was_error(false) 0039 { 0040 } 0041 unsigned long first_request; 0042 Display *display; 0043 bool was_error; 0044 XErrorEvent error_event; 0045 }; 0046 0047 KXErrorHandler **KXErrorHandler::handlers = nullptr; 0048 int KXErrorHandler::pos = 0; 0049 int KXErrorHandler::size = 0; 0050 0051 KXErrorHandler::KXErrorHandler(Display *dpy) 0052 : user_handler1(nullptr), 0053 user_handler2(nullptr), 0054 old_handler(XSetErrorHandler(handler_wrapper)), 0055 d(new KXErrorHandlerPrivate(dpy)) 0056 { 0057 addHandler(); 0058 } 0059 0060 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED 0061 KXErrorHandler::KXErrorHandler(bool (*handler)(int request, int error_code, unsigned long resource_id), Display *dpy) 0062 : user_handler1(handler), 0063 user_handler2(nullptr), 0064 old_handler(XSetErrorHandler(handler_wrapper)), 0065 d(new KXErrorHandlerPrivate(dpy)) 0066 { 0067 addHandler(); 0068 } 0069 #endif 0070 0071 KXErrorHandler::KXErrorHandler(int (*handler)(Display *, XErrorEvent *), Display *dpy) 0072 : user_handler1(nullptr), 0073 user_handler2(handler), 0074 old_handler(XSetErrorHandler(handler_wrapper)), 0075 d(new KXErrorHandlerPrivate(dpy)) 0076 { 0077 addHandler(); 0078 } 0079 0080 KXErrorHandler::~KXErrorHandler() 0081 { 0082 XSetErrorHandler(old_handler); 0083 Q_ASSERT_X(this == handlers[ pos - 1 ], "KXErrorHandler", "out of order"); 0084 --pos; 0085 delete d; 0086 } 0087 0088 void KXErrorHandler::addHandler() 0089 { 0090 if (size == pos) { 0091 size += 16; 0092 handlers = static_cast< KXErrorHandler ** >(realloc(handlers, size * sizeof(KXErrorHandler *))); 0093 } 0094 handlers[ pos++ ] = this; 0095 } 0096 0097 bool KXErrorHandler::error(bool sync) const 0098 { 0099 if (sync) { 0100 XSync(d->display, False); 0101 } 0102 return d->was_error; 0103 } 0104 0105 XErrorEvent KXErrorHandler::errorEvent() const 0106 { 0107 return d->error_event; 0108 } 0109 0110 int KXErrorHandler::handler_wrapper(Display *dpy, XErrorEvent *e) 0111 { 0112 --pos; 0113 int ret = handlers[ pos ]->handle(dpy, e); 0114 ++pos; 0115 return ret; 0116 } 0117 0118 int KXErrorHandler::handle(Display *dpy, XErrorEvent *e) 0119 { 0120 if (dpy == d->display 0121 // e->serial >= d->first_request , compare like X timestamps to handle wrapping 0122 && NET::timestampCompare(e->serial, d->first_request) >= 0) { 0123 // it's for us 0124 //qDebug( "Handling: %p", static_cast< void* >( this )); 0125 bool error = false; 0126 if (user_handler1 != nullptr) { 0127 if (user_handler1(e->request_code, e->error_code, e->resourceid)) { 0128 error = true; 0129 } 0130 } else if (user_handler2 != nullptr) { 0131 if (user_handler2(dpy, e) != 0) { 0132 error = true; 0133 } 0134 } else { // no handler set, simply set that there was an error 0135 error = true; 0136 } 0137 if (error && !d->was_error) { 0138 // only remember the first 0139 d->was_error = true; 0140 d->error_event = *e; 0141 } 0142 return 0; 0143 } 0144 //qDebug( "Going deeper: %p", static_cast< void* >( this )); 0145 return old_handler(dpy, e); 0146 } 0147 0148 QByteArray KXErrorHandler::errorMessage(const XErrorEvent &event, Display *dpy) 0149 { 0150 // "Error: <error> (<value>), Request: <request>(<value>), Resource: <value>" 0151 QByteArray ret; 0152 char tmp[ 256 ]; 0153 char num[ 256 ]; 0154 #if 0 // see below 0155 if (event.request_code < 128) // core request 0156 #endif 0157 { 0158 XGetErrorText(dpy, event.error_code, tmp, 255); 0159 if (char *paren = strchr(tmp, '(')) { // the explanation in parentheses just makes 0160 *paren = '\0'; // it more verbose and is not really useful 0161 } 0162 // the various casts are to get overloads non-ambiguous :-/ 0163 ret = QByteArray("error: ") + (const char *)tmp + '[' + QByteArray::number(event.error_code) + ']'; 0164 sprintf(num, "%d", event.request_code); 0165 XGetErrorDatabaseText(dpy, "XRequest", num, "<unknown>", tmp, 256); 0166 ret += QByteArray(", request: ") + (const char *)tmp + '[' + QByteArray::number(event.request_code) + ']'; 0167 if (event.resourceid != 0) { 0168 ret += QByteArray(", resource: 0x") + QByteArray::number((qlonglong)event.resourceid, 16); 0169 } 0170 } 0171 #if 0 0172 else { // extensions 0173 // XGetErrorText() currently has a bug that makes it fail to find text 0174 // for some errors (when error==error_base), also XGetErrorDatabaseText() 0175 // requires the right extension name, so it is needed to get info about 0176 // all extensions. However that is almost impossible: 0177 // - Xlib itself has it, but in internal data. 0178 // - Opening another X connection now can cause deadlock with server grabs. 0179 // - Fetching it at startup means a bunch of roundtrips. 0180 // So if this becomes more useful in the future, do the roundtrips at startup, 0181 // or fetch it in kded and export as an env.var or something. 0182 Display *dpy2 = XOpenDisplay(XDisplayString(dpy)); 0183 int nextensions; 0184 char **extensions = XListExtensions(dpy2, &nextensions); 0185 int *majors = NULL; 0186 int *error_bases = NULL; 0187 if (extensions == NULL) { 0188 nextensions = 0; 0189 } else { 0190 majors = new int[ nextensions ]; 0191 error_bases = new int[ nextensions ]; 0192 for (int i = 0; 0193 i < nextensions; 0194 ++i) { 0195 int dummy; 0196 if (!XQueryExtension(dpy2, extensions[ i ], &majors[ i ], &dummy, &error_bases[ i ])) { 0197 majors[ i ] = 0; 0198 error_bases[ i ] = 0; 0199 } 0200 } 0201 } 0202 XGetErrorText(dpy, event.error_code, tmp, 255); 0203 int index = -1; 0204 int base = 0; 0205 for (int i = 0; 0206 i < nextensions; 0207 ++i) 0208 if (error_bases[ i ] != 0 0209 && event.error_code >= error_bases[ i ] && (index == -1 || error_bases[ i ] > base)) { 0210 index = i; 0211 base = error_bases[ i ]; 0212 } 0213 if (tmp == QString::number(event.error_code)) { // XGetErrorText() failed, 0214 // or it has a bug that causes not finding all errors, check ourselves 0215 if (index != -1) { 0216 qsnprintf(num, 255, "%s.%d", extensions[ index ], event.error_code - base); 0217 XGetErrorDatabaseText(dpy, "XProtoError", num, "<unknown>", tmp, 255); 0218 } else { 0219 strcpy(tmp, "<unknown>"); 0220 } 0221 } 0222 if (char *paren = strchr(tmp, '(')) { 0223 *paren = '\0'; 0224 } 0225 if (index != -1) 0226 ret = QByteArray("error: ") + (const char *)tmp + '[' + (const char *)extensions[ index ] 0227 + '+' + QByteArray::number(event.error_code - base) + ']'; 0228 else { 0229 ret = QByteArray("error: ") + (const char *)tmp + '[' + QByteArray::number(event.error_code) + ']'; 0230 } 0231 tmp[ 0 ] = '\0'; 0232 for (int i = 0; 0233 i < nextensions; 0234 ++i) 0235 if (majors[ i ] == event.request_code) { 0236 qsnprintf(num, 255, "%s.%d", extensions[ i ], event.minor_code); 0237 XGetErrorDatabaseText(dpy, "XRequest", num, "<unknown>", tmp, 255); 0238 ret += QByteArray(", request: ") + (const char *)tmp + '[' + (const char *)extensions[ i ] + '+' 0239 + QByteArray::number(event.minor_code) + ']'; 0240 } 0241 if (tmp[ 0 ] == '\0') // not found??? 0242 ret += QByteArray(", request <unknown> [") + QByteArray::number(event.request_code) + ':' 0243 + QByteArray::number(event.minor_code) + ']'; 0244 if (event.resourceid != 0) { 0245 ret += QByteArray(", resource: 0x") + QByteArray::number((qlonglong)event.resourceid, 16); 0246 } 0247 if (extensions != NULL) { 0248 XFreeExtensionList(extensions); 0249 } 0250 delete[] majors; 0251 delete[] error_bases; 0252 XCloseDisplay(dpy2); 0253 } 0254 #endif 0255 return ret; 0256 } 0257 0258 Display *KXErrorHandler::display() 0259 { 0260 return QX11Info::display(); 0261 }